melib: Add standard heeder constants in email::headers
Like `http` crate doespull/223/head
parent
1eea8bab77
commit
235fceaf21
|
@ -56,7 +56,9 @@ pub fn override_derive(filenames: &[(&str, &str)]) {
|
|||
#![allow(clippy::derivable_impls)]
|
||||
|
||||
//! This module is automatically generated by config_macros.rs.
|
||||
|
||||
use super::*;
|
||||
use melib::HeaderName;
|
||||
|
||||
"##
|
||||
.to_string();
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
/*! Compose a `Draft`, with MIME and attachment support */
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
ffi::OsStr,
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
|
@ -59,18 +60,18 @@ impl Default for Draft {
|
|||
fn default() -> Self {
|
||||
let mut headers = HeaderMap::default();
|
||||
headers.insert(
|
||||
HeaderName::new_unchecked("Date"),
|
||||
HeaderName::DATE,
|
||||
crate::datetime::timestamp_to_string(
|
||||
crate::datetime::now(),
|
||||
Some(crate::datetime::RFC822_DATE),
|
||||
true,
|
||||
),
|
||||
);
|
||||
headers.insert(HeaderName::new_unchecked("From"), "".into());
|
||||
headers.insert(HeaderName::new_unchecked("To"), "".into());
|
||||
headers.insert(HeaderName::new_unchecked("Cc"), "".into());
|
||||
headers.insert(HeaderName::new_unchecked("Bcc"), "".into());
|
||||
headers.insert(HeaderName::new_unchecked("Subject"), "".into());
|
||||
headers.insert(HeaderName::FROM, "".into());
|
||||
headers.insert(HeaderName::TO, "".into());
|
||||
headers.insert(HeaderName::CC, "".into());
|
||||
headers.insert(HeaderName::BCC, "".into());
|
||||
headers.insert(HeaderName::SUBJECT, "".into());
|
||||
|
||||
Draft {
|
||||
headers,
|
||||
|
@ -118,12 +119,20 @@ impl Draft {
|
|||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn set_header(&mut self, header: &str, value: String) -> &mut Self {
|
||||
self.headers
|
||||
.insert(HeaderName::new_unchecked(header), value);
|
||||
pub fn set_header(&mut self, header: HeaderName, value: String) -> &mut Self {
|
||||
self.headers.insert(header, value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn try_set_header(
|
||||
&mut self,
|
||||
header: &str,
|
||||
value: String,
|
||||
) -> std::result::Result<&mut Self, InvalidHeaderName> {
|
||||
self.headers.insert(HeaderName::try_from(header)?, value);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn set_wrap_header_preamble(&mut self, value: Option<(String, String)>) -> &mut Self {
|
||||
self.wrap_header_preamble = value;
|
||||
self
|
||||
|
@ -160,7 +169,7 @@ impl Draft {
|
|||
pub fn new_reply(envelope: &Envelope, bytes: &[u8], reply_to_all: bool) -> Self {
|
||||
let mut ret = Draft::default();
|
||||
ret.headers_mut().insert(
|
||||
HeaderName::new_unchecked("References"),
|
||||
HeaderName::REFERENCES,
|
||||
format!(
|
||||
"{} {}",
|
||||
envelope
|
||||
|
@ -177,7 +186,7 @@ impl Draft {
|
|||
),
|
||||
);
|
||||
ret.headers_mut().insert(
|
||||
HeaderName::new_unchecked("In-Reply-To"),
|
||||
HeaderName::IN_REPLY_TO,
|
||||
envelope.message_id_display().into(),
|
||||
);
|
||||
// "Mail-Followup-To/(To+Cc+(Mail-Reply-To/Reply-To/From)) for follow-up,
|
||||
|
@ -186,33 +195,27 @@ impl Draft {
|
|||
if reply_to_all {
|
||||
if let Some(reply_to) = envelope.other_headers().get("Mail-Followup-To") {
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::new_unchecked("To"), reply_to.to_string());
|
||||
.insert(HeaderName::TO, reply_to.to_string());
|
||||
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::new_unchecked("To"), reply_to.to_string());
|
||||
.insert(HeaderName::TO, reply_to.to_string());
|
||||
} else {
|
||||
ret.headers_mut().insert(
|
||||
HeaderName::new_unchecked("To"),
|
||||
envelope.field_from_to_string(),
|
||||
);
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::TO, envelope.field_from_to_string());
|
||||
}
|
||||
// FIXME: add To/Cc
|
||||
} else if let Some(reply_to) = envelope.other_headers().get("Mail-Reply-To") {
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::new_unchecked("To"), reply_to.to_string());
|
||||
.insert(HeaderName::TO, reply_to.to_string());
|
||||
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::new_unchecked("To"), reply_to.to_string());
|
||||
.insert(HeaderName::TO, reply_to.to_string());
|
||||
} else {
|
||||
ret.headers_mut().insert(
|
||||
HeaderName::new_unchecked("To"),
|
||||
envelope.field_from_to_string(),
|
||||
);
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::TO, envelope.field_from_to_string());
|
||||
}
|
||||
ret.headers_mut().insert(
|
||||
HeaderName::new_unchecked("Cc"),
|
||||
envelope.field_cc_to_string(),
|
||||
);
|
||||
ret.headers_mut()
|
||||
.insert(HeaderName::CC, envelope.field_cc_to_string());
|
||||
let body = envelope.body_bytes(bytes);
|
||||
ret.body = {
|
||||
let reply_body_bytes = body.decode_rec(Default::default());
|
||||
|
@ -221,7 +224,7 @@ impl Draft {
|
|||
let mut ret = format!(
|
||||
"On {} {} wrote:\n",
|
||||
envelope.date_as_str(),
|
||||
&ret.headers()["To"]
|
||||
&ret.headers()[HeaderName::TO]
|
||||
);
|
||||
for l in lines {
|
||||
ret.push('>');
|
||||
|
@ -304,10 +307,8 @@ impl Draft {
|
|||
if let Ok((_, addr)) = super::parser::address::mailbox(self.headers["From"].as_bytes())
|
||||
{
|
||||
if let Some(fqdn) = addr.get_fqdn() {
|
||||
self.headers.insert(
|
||||
HeaderName::new_unchecked("Message-ID"),
|
||||
random::gen_message_id(&fqdn),
|
||||
);
|
||||
self.headers
|
||||
.insert(HeaderName::MESSAGE_ID, random::gen_message_id(&fqdn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -559,8 +560,8 @@ mod tests {
|
|||
default
|
||||
.set_wrap_header_preamble(Some(("<!--".to_string(), "-->".to_string())))
|
||||
.set_body("αδφαφσαφασ".to_string())
|
||||
.set_header("Subject", "test_update()".into())
|
||||
.set_header("Date", "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
.set_header(HeaderName::SUBJECT, "test_update()".into())
|
||||
.set_header(HeaderName::DATE, "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
|
||||
let original = default.clone();
|
||||
let s = default.to_edit_string();
|
||||
|
|
|
@ -20,102 +20,18 @@
|
|||
*/
|
||||
|
||||
/*! Wrapper type `HeaderName` for case-insensitive comparisons */
|
||||
|
||||
pub mod names;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::{Eq, PartialEq},
|
||||
convert::TryFrom,
|
||||
fmt,
|
||||
convert::{TryFrom, TryInto},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct HeaderNameType<S>(S);
|
||||
|
||||
/// Case insensitive wrapper for a header name. As of `RFC5322` it's
|
||||
/// guaranteed to be ASCII.
|
||||
pub type HeaderName = HeaderNameType<SmallVec<[u8; 32]>>;
|
||||
|
||||
impl HeaderName {
|
||||
pub fn new_unchecked(from: &str) -> Self {
|
||||
HeaderNameType(from.as_bytes().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<[u8]>> fmt::Display for HeaderNameType<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.normalize())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<[u8]>> fmt::Debug for HeaderNameType<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<[u8]>> PartialEq<[u8]> for HeaderNameType<S> {
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
self.0.as_ref().eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<[u8]>> PartialEq<&str> for HeaderNameType<S> {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
self.0.as_ref().eq_ignore_ascii_case(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S1: AsRef<[u8]>, S2: AsRef<[u8]>> PartialEq<HeaderNameType<S2>> for HeaderNameType<S1> {
|
||||
fn eq(&self, other: &HeaderNameType<S2>) -> bool {
|
||||
self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<[u8]>> Eq for HeaderNameType<S> {}
|
||||
|
||||
impl<S: AsRef<[u8]>> Hash for HeaderNameType<S> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
for b in self.0.as_ref().iter() {
|
||||
b.to_ascii_lowercase().hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for HeaderName {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
if value.is_ascii() {
|
||||
Ok(HeaderNameType(value.into()))
|
||||
} else {
|
||||
Err(Error::new(format!(
|
||||
"Header value is not ascii: {:?}",
|
||||
value
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for HeaderName {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
if value.is_ascii() {
|
||||
Ok(HeaderNameType(value.as_bytes().into()))
|
||||
} else {
|
||||
Err(Error::new(format!(
|
||||
"Header value is not ascii: {:?}",
|
||||
value
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use names::{HeaderName, InvalidHeaderName, Protocol, Standard, StandardHeader, Status};
|
||||
|
||||
trait HeaderKey {
|
||||
fn to_key(&self) -> &[u8];
|
||||
|
@ -137,9 +53,9 @@ impl PartialEq for dyn HeaderKey + '_ {
|
|||
|
||||
impl Eq for dyn HeaderKey + '_ {}
|
||||
|
||||
impl<S: AsRef<[u8]>> HeaderKey for HeaderNameType<S> {
|
||||
impl HeaderKey for HeaderName {
|
||||
fn to_key(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,119 +67,94 @@ impl<'a> Borrow<dyn HeaderKey + 'a> for HeaderName {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<[u8]>> HeaderNameType<S> {
|
||||
pub fn as_str(&self) -> &str {
|
||||
// HeadersType are ascii so valid utf8
|
||||
unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
|
||||
}
|
||||
|
||||
pub fn normalize(&self) -> &str {
|
||||
if self == &b"subject"[..] {
|
||||
"Subject"
|
||||
} else if self == &b"from"[..] {
|
||||
"From"
|
||||
} else if self == &b"to"[..] {
|
||||
"To"
|
||||
} else if self == &b"cc"[..] {
|
||||
"Cc"
|
||||
} else if self == &b"bcc"[..] {
|
||||
"Bcc"
|
||||
} else if self == &b"reply-to"[..] {
|
||||
"Reply-To"
|
||||
} else if self == &b"in-reply-to"[..] {
|
||||
"In-Reply-To"
|
||||
} else if self == &b"references"[..] {
|
||||
"References"
|
||||
} else if self == &b"sender"[..] {
|
||||
"Sender"
|
||||
} else if self == &b"mail-reply-to"[..] {
|
||||
"Mail-Reply-To"
|
||||
} else if self == &b"mail-followup-to"[..] {
|
||||
"Mail-Followup-To"
|
||||
} else if self == &b"mime-version"[..] {
|
||||
"MIME-Version"
|
||||
} else if self == &b"content-disposition"[..] {
|
||||
"Content-Disposition"
|
||||
} else if self == &b"content-transfer-encoding"[..] {
|
||||
"Content-Transfer-Encoding"
|
||||
} else if self == &b"content-type"[..] {
|
||||
"Content-Type"
|
||||
} else if self == &b"content-id"[..] {
|
||||
"Content-ID"
|
||||
} else if self == &b"content-description"[..] {
|
||||
"Content-Description"
|
||||
} else if self == &b"authentication-results"[..] {
|
||||
"Authentication-Results"
|
||||
} else if self == &b"dkim-signature"[..] {
|
||||
"DKIM-Signature"
|
||||
} else if self == &b"delivered-to"[..] {
|
||||
"Delivered-To"
|
||||
} else if self == &b"message-id"[..] {
|
||||
"Message-ID"
|
||||
} else if self == &b"comments"[..] {
|
||||
"Comments"
|
||||
} else if self == &b"keywords"[..] {
|
||||
"Keywords"
|
||||
} else if self == &b"resent-from"[..] {
|
||||
"Resent-From"
|
||||
} else if self == &b"resent-sender"[..] {
|
||||
"Resent-Sender"
|
||||
} else if self == &b"resent-to"[..] {
|
||||
"Resent-To"
|
||||
} else if self == &b"resent-cc"[..] {
|
||||
"Resent-Cc"
|
||||
} else if self == &b"resent-bcc"[..] {
|
||||
"Resent-Bcc"
|
||||
} else if self == &b"resent-date"[..] {
|
||||
"Resent-Date"
|
||||
} else if self == &b"resent-message-id"[..] {
|
||||
"Resent-Message-ID"
|
||||
} else if self == &b"resent-reply-to"[..] {
|
||||
"Resent-Reply-To"
|
||||
} else if self == &b"return-path"[..] {
|
||||
"Return-Path"
|
||||
} else if self == &b"received"[..] {
|
||||
"Received"
|
||||
} else {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Map of mail headers and values.
|
||||
///
|
||||
/// Can be indexed by:
|
||||
///
|
||||
/// - `usize`
|
||||
/// - `&[u8]`, which panics if it's not a valid header value.
|
||||
/// - `&str`, which also panics if it's not a valid header value.
|
||||
/// - [HeaderName], which is guaranteed to be valid.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Except for the above, indexing will also panic if index is out of range or
|
||||
/// header key is not present in the map.
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct HeaderMap(indexmap::IndexMap<HeaderName, String>);
|
||||
|
||||
impl std::ops::Index<usize> for HeaderMap {
|
||||
type Output = str;
|
||||
fn index(&self, k: usize) -> &Self::Output {
|
||||
(self.0)[k].as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<&[u8]> for HeaderMap {
|
||||
type Output = str;
|
||||
fn index(&self, k: &[u8]) -> &Self::Output {
|
||||
(self.0)[HeaderNameType(k).borrow() as &dyn HeaderKey].as_str()
|
||||
(self.0)[HeaderName::try_from(k)
|
||||
.expect("Invalid bytes in header name.")
|
||||
.borrow() as &dyn HeaderKey]
|
||||
.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<&str> for HeaderMap {
|
||||
type Output = str;
|
||||
fn index(&self, k: &str) -> &Self::Output {
|
||||
(self.0)[HeaderNameType(k).borrow() as &dyn HeaderKey].as_str()
|
||||
(self.0)[&HeaderName::try_from(k).expect("Invalid bytes in header name.")].as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<&HeaderName> for HeaderMap {
|
||||
type Output = str;
|
||||
fn index(&self, k: &HeaderName) -> &Self::Output {
|
||||
(self.0)[k].as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<HeaderName> for HeaderMap {
|
||||
type Output = str;
|
||||
fn index(&self, k: HeaderName) -> &Self::Output {
|
||||
(self.0)[&k].as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderMap {
|
||||
pub fn get_mut(&mut self, key: &str) -> Option<&mut String> {
|
||||
(self.0).get_mut(HeaderNameType(key).borrow() as &dyn HeaderKey)
|
||||
pub fn get_mut<T: TryInto<HeaderName> + std::fmt::Debug>(
|
||||
&mut self,
|
||||
key: T,
|
||||
) -> Option<&mut String>
|
||||
where
|
||||
<T as TryInto<HeaderName>>::Error: std::fmt::Debug,
|
||||
{
|
||||
let k = key.try_into().expect("Invalid bytes in header name.");
|
||||
(self.0).get_mut(&k)
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Option<&str> {
|
||||
(self.0)
|
||||
.get(HeaderNameType(key).borrow() as &dyn HeaderKey)
|
||||
.map(|x| x.as_str())
|
||||
pub fn get<T: TryInto<HeaderName> + std::fmt::Debug>(&self, key: T) -> Option<&str>
|
||||
where
|
||||
<T as TryInto<HeaderName>>::Error: std::fmt::Debug,
|
||||
{
|
||||
let k = key.try_into().expect("Invalid bytes in header name.");
|
||||
(self.0).get(&k).map(|x| x.as_str())
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, key: &str) -> bool {
|
||||
(self.0).contains_key(HeaderNameType(key).borrow() as &dyn HeaderKey)
|
||||
pub fn contains_key<T: TryInto<HeaderName> + std::fmt::Debug>(&self, key: T) -> bool
|
||||
where
|
||||
<T as TryInto<HeaderName>>::Error: std::fmt::Debug,
|
||||
{
|
||||
let k = key.try_into().expect("Invalid bytes in header name.");
|
||||
(self.0).contains_key(&k)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: &str) -> Option<String> {
|
||||
(self.0).remove(HeaderNameType(key).borrow() as &dyn HeaderKey)
|
||||
pub fn remove<T: TryInto<HeaderName> + std::fmt::Debug>(&mut self, key: T) -> Option<String>
|
||||
where
|
||||
<T as TryInto<HeaderName>>::Error: std::fmt::Debug,
|
||||
{
|
||||
let k = key.try_into().expect("Invalid bytes in header name.");
|
||||
(self.0).remove(&k)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,800 @@
|
|||
/*
|
||||
* meli - melib crate.
|
||||
*
|
||||
* Copyright 2023 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*! E-mail header names. Also referred to as Fields in [RFC5322] */
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use std::{
|
||||
borrow::{Borrow, Cow},
|
||||
convert::TryFrom,
|
||||
error::Error,
|
||||
hash::{Hash, Hasher},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::email::parser::BytesExt;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default, Serialize, Deserialize)]
|
||||
pub struct Protocol: u32 {
|
||||
const None = 0b00000001;
|
||||
const Mail = Self::None.bits() << 1;
|
||||
const NNTP = Self::Mail.bits() << 1;
|
||||
const MIME = Self::NNTP.bits() << 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Case insensitive wrapper for a header name. As of `RFC5322` it's
|
||||
/// guaranteed to be ASCII.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct HeaderName {
|
||||
inner: Repr<Custom>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
enum Repr<T> {
|
||||
Standard(StandardHeader),
|
||||
Custom(T),
|
||||
}
|
||||
|
||||
// Used to hijack the Hash impl
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
struct Custom(SmallVec<[u8; 32]>);
|
||||
|
||||
/// A possible error when converting a `HeaderName` from another type.
|
||||
pub struct InvalidHeaderName;
|
||||
|
||||
impl Error for InvalidHeaderName {}
|
||||
|
||||
impl std::fmt::Debug for InvalidHeaderName {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(fmt, "{}", "Invalid header name.")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidHeaderName {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(fmt, "{}", stringify!(InvalidHeaderName))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! standard_headers {
|
||||
(
|
||||
$(
|
||||
$(#[$docs:meta])*
|
||||
($konst:ident, $upcase:ident, $name:literal, $template:expr, $(Protocol::$var:tt)|+,$status:expr,$standards:expr);
|
||||
)+
|
||||
) => {
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum StandardHeader {
|
||||
$(
|
||||
$konst,
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
$(#[$docs])*
|
||||
pub const $upcase: HeaderName = HeaderName {
|
||||
inner: Repr::Standard(StandardHeader::$konst),
|
||||
};
|
||||
)+
|
||||
|
||||
impl HeaderName {
|
||||
$(
|
||||
pub const $upcase: Self = $upcase;
|
||||
)+
|
||||
}
|
||||
|
||||
impl StandardHeader {
|
||||
#[inline]
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
$(
|
||||
Self::$konst => $name,
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn protocol(&self) -> Protocol {
|
||||
match *self {
|
||||
$(
|
||||
Self::$konst => Protocol::from_bits_truncate($(Protocol::$var.bits()|)* u32::MAX),
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn status(&self) -> Status {
|
||||
match *self {
|
||||
$(
|
||||
Self::$konst => $status,
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn standards(&self) -> &[Standard] {
|
||||
match *self {
|
||||
$(
|
||||
Self::$konst => $standards,
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(name_bytes: &[u8]) -> Option<Self> {
|
||||
match name_bytes {
|
||||
$(
|
||||
_ if name_bytes.eq_ignore_ascii_case($name.as_bytes()) => Some(Self::$konst),
|
||||
)+
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
const TEST_HEADERS: &[(StandardHeader, &str)] = &[
|
||||
$(
|
||||
(StandardHeader::$konst, $name),
|
||||
)+
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_parse_standard_headers() {
|
||||
for &(std, name) in TEST_HEADERS {
|
||||
// Test lower case
|
||||
assert_eq!(HeaderName::from_bytes(name.as_bytes()).unwrap(), HeaderName::from(std));
|
||||
|
||||
// Test upper case
|
||||
let upper = std::str::from_utf8(name.as_bytes()).expect("byte string constants are all utf-8").to_uppercase();
|
||||
assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(), HeaderName::from(std));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standard_headers_into_bytes() {
|
||||
//for &(std, name) in TEST_HEADERS {
|
||||
//let name = std::str::from_utf8(name.as_bytes()).unwrap();
|
||||
//let std = HeaderName::from(std);
|
||||
// Test lower case
|
||||
//let bytes: Bytes =
|
||||
// HeaderName::from_bytes(name.as_bytes()).unwrap().inner.into();
|
||||
//assert_eq!(bytes, name);
|
||||
//assert_eq!(HeaderName::from_bytes(name.as_bytes()).unwrap(), std);
|
||||
|
||||
// Test upper case
|
||||
// let upper = name.to_uppercase();
|
||||
//let bytes: Bytes =
|
||||
// HeaderName::from_bytes(upper.as_bytes()).unwrap().inner.into();
|
||||
//assert_eq!(bytes, name.as_bytes());
|
||||
//assert_eq!(HeaderName::from_bytes(upper.as_bytes()).unwrap(),
|
||||
// std);
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! standards {
|
||||
(
|
||||
$(
|
||||
$(#[$docs:meta])*
|
||||
($konst:ident, $upcase:ident, $name:literal, $lowername:literal );
|
||||
)+
|
||||
) => {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Standard {
|
||||
$(
|
||||
$konst,
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
$(#[$docs])*
|
||||
pub const $upcase: Standard = Standard::$konst;
|
||||
)+
|
||||
|
||||
impl Standard {
|
||||
#[inline]
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match *self {
|
||||
$(
|
||||
Self::$konst => $name,
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn url(&self) -> &str {
|
||||
match *self {
|
||||
$(
|
||||
Self::$konst => concat!("https://www.rfc-editor.org/rfc/", $lowername, ".html"),
|
||||
)+
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn from_bytes(name_bytes: &[u8]) -> Option<Self> {
|
||||
match name_bytes {
|
||||
$(
|
||||
_ if name_bytes.eq_ignore_ascii_case($name.as_bytes()) => Some(Self::$konst),
|
||||
)+
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
standards! {
|
||||
(RFC0850, RFC0850, "RFC0850", "rfc0850");
|
||||
(RFC1808, RFC1808, "RFC1808", "rfc1808");
|
||||
(RFC1849, RFC1849, "RFC1849", "rfc1849");
|
||||
(RFC2068, RFC2068, "RFC2068", "rfc2068");
|
||||
(RFC2076, RFC2076, "RFC2076", "rfc2076");
|
||||
(RFC2110, RFC2110, "RFC2110", "rfc2110");
|
||||
(RFC2156, RFC2156, "RFC2156", "rfc2156");
|
||||
(RFC2557, RFC2557, "RFC2557", "rfc2557");
|
||||
(RFC2616, RFC2616, "RFC2616", "rfc2616");
|
||||
(RFC2980, RFC2980, "RFC2980", "rfc2980");
|
||||
(RFC3798, RFC3798, "RFC3798", "rfc3798");
|
||||
(RFC3834, RFC3834, "RFC3834", "rfc3834");
|
||||
(RFC3865, RFC3865, "RFC3865", "rfc3865");
|
||||
(RFC3977, RFC3977, "RFC3977", "rfc3977");
|
||||
(RFC4021, RFC4021, "RFC4021", "rfc4021");
|
||||
(RFC5064, RFC5064, "RFC5064", "rfc5064");
|
||||
(RFC5321, RFC5321, "RFC5321", "rfc5321");
|
||||
(RFC5322, RFC5322, "RFC5322", "rfc5322");
|
||||
(RFC5337, RFC5337, "RFC5337", "rfc5337");
|
||||
(RFC5504, RFC5504, "RFC5504", "rfc5504");
|
||||
(RFC5518, RFC5518, "RFC5518", "rfc5518");
|
||||
(RFC5536, RFC5536, "RFC5536", "rfc5536");
|
||||
(RFC5537, RFC5537, "RFC5537", "rfc5537");
|
||||
(RFC5703, RFC5703, "RFC5703", "rfc5703");
|
||||
(RFC6017, RFC6017, "RFC6017", "rfc6017");
|
||||
(RFC6068, RFC6068, "RFC6068", "rfc6068");
|
||||
(RFC6109, RFC6109, "RFC6109", "rfc6109");
|
||||
(RFC6376, RFC6376, "RFC6376", "rfc6376");
|
||||
(RFC6477, RFC6477, "RFC6477", "rfc6477");
|
||||
(RFC6758, RFC6758, "RFC6758", "rfc6758");
|
||||
(RFC6854, RFC6854, "RFC6854", "rfc6854");
|
||||
(RFC6857, RFC6857, "RFC6857", "rfc6857");
|
||||
(RFC7208, RFC7208, "RFC7208", "rfc7208");
|
||||
(RFC7259, RFC7259, "RFC7259", "rfc7259");
|
||||
(RFC7293, RFC7293, "RFC7293", "rfc7293");
|
||||
(RFC7444, RFC7444, "RFC7444", "rfc7444");
|
||||
(RFC7681, RFC7681, "RFC7681", "rfc7681");
|
||||
(RFC8058, RFC8058, "RFC8058", "rfc8058");
|
||||
(RFC8255, RFC8255, "RFC8255", "rfc8255");
|
||||
(RFC8315, RFC8315, "RFC8315", "rfc8315");
|
||||
(RFC8460, RFC8460, "RFC8460", "rfc8460");
|
||||
(RFC8601, RFC8601, "RFC8601", "rfc8601");
|
||||
(RFC8617, RFC8617, "RFC8617", "rfc8617");
|
||||
(RFC8689, RFC8689, "RFC8689", "rfc8689");
|
||||
(RFC9057, RFC9057, "RFC9057", "rfc9057");
|
||||
(RFC9228, RFC9228, "RFC9228", "rfc9228");
|
||||
}
|
||||
|
||||
/// Status of field at the moment of writing.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum Status {
|
||||
/// Deprecated,
|
||||
Deprecated,
|
||||
/// Experimental,
|
||||
Experimental,
|
||||
/// Informational,
|
||||
Informational,
|
||||
/// None,
|
||||
None,
|
||||
/// Obsoleted,
|
||||
Obsoleted,
|
||||
/// Reserved,
|
||||
Reserved,
|
||||
/// Standard,
|
||||
Standard,
|
||||
}
|
||||
|
||||
// Generate constants for all standard e-mail field headers.
|
||||
standard_headers! {
|
||||
/* Unit Variant |Constant ident |Actual field value |Template value |Protocols |Status |Standards */
|
||||
/* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
|
||||
(Subject, SUBJECT, "Subject", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5536, Standard::RFC5322]);
|
||||
(ReplyTo, REPLY_TO, "Reply-To", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5536, Standard::RFC5322]);
|
||||
(InReplyTo, IN_REPLY_TO, "In-Reply-To", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(References, REFERENCES, "References", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5536, Standard::RFC5322]);
|
||||
(MailReplyTo, MAIL_REPLY_TO, "Mail-Reply-To", None, Protocol::Mail, Status::None, &[]);
|
||||
(MailFollowupTo, MAIL_FOLLOWUP_TO, "Mail-Followup-To", None, Protocol::Mail, Status::None, &[]);
|
||||
(DeliveredTo, DELIVERED_TO, "Delivered-To", None, Protocol::Mail, Status::None, &[Standard::RFC9228]);
|
||||
(Comments, COMMENTS, "Comments", None, Protocol::Mail, Status::None, &[]);
|
||||
(Keywords, KEYWORDS, "Keywords", None, Protocol::Mail, Status::None, &[]);
|
||||
(Received, RECEIVED, "Received", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322, Standard::RFC5321]);
|
||||
(ContentLanguage, CONTENT_LANGUAGE, "Content-Language", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentLength, CONTENT_LENGTH, "Content-Length", None, Protocol::Mail, Status::None, &[]);
|
||||
(Forwarded, FORWARDED, "Forwarded", None, Protocol::Mail, Status::None, &[]);
|
||||
(AcceptLanguage, ACCEPT_LANGUAGE, "Accept-Language", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(AlsoControl, ALSO_CONTROL, "Also-Control", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC1849, Standard::RFC5536]);
|
||||
(AlternateRecipient, ALTERNATE_RECIPIENT, "Alternate-Recipient", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Approved, APPROVED, "Approved", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(ArcAuthenticationResults, ARC_AUTHENTICATION_RESULTS, "ARC-Authentication-Results", None, Protocol::Mail, Status::Experimental, &[Standard::RFC8617]);
|
||||
(ArcMessageSignature, ARC_MESSAGE_SIGNATURE, "ARC-Message-Signature", None, Protocol::Mail, Status::Experimental, &[Standard::RFC8617]);
|
||||
(ArcSeal, ARC_SEAL, "ARC-Seal", None, Protocol::Mail, Status::Experimental, &[Standard::RFC8617]);
|
||||
(Archive, ARCHIVE, "Archive", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(ArchivedAt, ARCHIVED_AT, "Archived-At", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5064]);
|
||||
(ArticleNames, ARTICLE_NAMES, "Article-Names", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC1849, Standard::RFC5536]);
|
||||
(ArticleUpdates, ARTICLE_UPDATES, "Article-Updates", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC1849, Standard::RFC5536]);
|
||||
(AuthenticationResults, AUTHENTICATION_RESULTS, "Authentication-Results", None, Protocol::Mail, Status::Standard, &[Standard::RFC8601]);
|
||||
(AutoSubmitted, AUTO_SUBMITTED, "Auto-Submitted", None, Protocol::Mail, Status::Standard, &[Standard::RFC3834]);
|
||||
(Autoforwarded, AUTOFORWARDED, "Autoforwarded", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Autosubmitted, AUTOSUBMITTED, "Autosubmitted", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Base, BASE, "Base", None, Protocol::MIME, Status::Obsoleted, &[Standard::RFC1808, Standard::RFC2068]);
|
||||
(Bcc, BCC, "Bcc", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(Body, BODY, "Body", None, Protocol::None, Status::Reserved, &[Standard::RFC6068]);
|
||||
(CancelKey, CANCEL_KEY, "Cancel-Key", None, Protocol::NNTP, Status::Standard, &[Standard::RFC8315]);
|
||||
(CancelLock, CANCEL_LOCK, "Cancel-Lock", None, Protocol::NNTP, Status::Standard, &[Standard::RFC8315]);
|
||||
(Cc, CC, "Cc", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(ContentAlternative, CONTENT_ALTERNATIVE, "Content-Alternative", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentBase, CONTENT_BASE, "Content-Base", None, Protocol::MIME, Status::Obsoleted, &[Standard::RFC2110, Standard::RFC2557]);
|
||||
(ContentDescription, CONTENT_DESCRIPTION, "Content-Description", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentDisposition, CONTENT_DISPOSITION, "Content-Disposition", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentDuration, CONTENT_DURATION, "Content-Duration", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentFeatures, CONTENT_FEATURES, "Content-Features", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentId, CONTENT_ID, "Content-ID", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentIdentifier, CONTENT_IDENTIFIER, "Content-Identifier", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ContentLocation, CONTENT_LOCATION, "Content-Location", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentMd5, CONTENT_MD5, "Content-MD5", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentReturn, CONTENT_RETURN, "Content-Return", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ContentTransferEncoding, CONTENT_TRANSFER_ENCODING, "Content-Transfer-Encoding", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(ContentTranslationType, CONTENT_TRANSLATION_TYPE, "Content-Translation-Type", None, Protocol::MIME, Status::Standard, &[Standard::RFC8255]);
|
||||
(ContentType, CONTENT_TYPE, "Content-Type", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(Control, CONTROL, "Control", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(Conversion, CONVERSION, "Conversion", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ConversionWithLoss, CONVERSION_WITH_LOSS, "Conversion-With-Loss", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DlExpansionHistory, DL_EXPANSION_HISTORY, "DL-Expansion-History", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Date, DATE, "Date", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5536, Standard::RFC5322]);
|
||||
(DateReceived, DATE_RECEIVED, "Date-Received", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC0850, Standard::RFC5536]);
|
||||
(DeferredDelivery, DEFERRED_DELIVERY, "Deferred-Delivery", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DeliveryDate, DELIVERY_DATE, "Delivery-Date", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DiscardedX400IpmsExtensions, DISCARDED_X400_IPMS_EXTENSIONS, "Discarded-X400-IPMS-Extensions", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DiscardedX400MtsExtensions, DISCARDED_X400_MTS_EXTENSIONS, "Discarded-X400-MTS-Extensions", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DiscloseRecipients, DISCLOSE_RECIPIENTS, "Disclose-Recipients", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DispositionNotificationOptions, DISPOSITION_NOTIFICATION_OPTIONS, "Disposition-Notification-Options", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(DispositionNotificationTo, DISPOSITION_NOTIFICATION_TO, "Disposition-Notification-To", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Distribution, DISTRIBUTION, "Distribution", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(DkimSignature, DKIM_SIGNATURE, "DKIM-Signature", None, Protocol::Mail, Status::Standard, &[Standard::RFC6376]);
|
||||
(DowngradedBcc, DOWNGRADED_BCC, "Downgraded-Bcc", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedCc, DOWNGRADED_CC, "Downgraded-Cc", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedDispositionNotificationTo, DOWNGRADED_DISPOSITION_NOTIFICATION_TO, "Downgraded-Disposition-Notification-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedFinalRecipient, DOWNGRADED_FINAL_RECIPIENT, "Downgraded-Final-Recipient", None, Protocol::Mail, Status::Standard, &[Standard::RFC6857]);
|
||||
(DowngradedFrom, DOWNGRADED_FROM, "Downgraded-From", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedInReplyTo, DOWNGRADED_IN_REPLY_TO, "Downgraded-In-Reply-To", None, Protocol::Mail, Status::Standard, &[Standard::RFC6857]);
|
||||
(DowngradedMailFrom, DOWNGRADED_MAIL_FROM, "Downgraded-Mail-From", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedMessageId, DOWNGRADED_MESSAGE_ID, "Downgraded-Message-Id", None, Protocol::Mail, Status::Standard, &[Standard::RFC6857]);
|
||||
(DowngradedOriginalRecipient, DOWNGRADED_ORIGINAL_RECIPIENT, "Downgraded-Original-Recipient", None, Protocol::Mail, Status::Standard, &[Standard::RFC6857]);
|
||||
(DowngradedRcptTo, DOWNGRADED_RCPT_TO, "Downgraded-Rcpt-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedReferences, DOWNGRADED_REFERENCES, "Downgraded-References", None, Protocol::Mail, Status::Standard, &[Standard::RFC6857]);
|
||||
(DowngradedReplyTo, DOWNGRADED_REPLY_TO, "Downgraded-Reply-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedResentBcc, DOWNGRADED_RESENT_BCC, "Downgraded-Resent-Bcc", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedResentCc, DOWNGRADED_RESENT_CC, "Downgraded-Resent-Cc", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedResentFrom, DOWNGRADED_RESENT_FROM, "Downgraded-Resent-From", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedResentReplyTo, DOWNGRADED_RESENT_REPLY_TO, "Downgraded-Resent-Reply-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedResentSender, DOWNGRADED_RESENT_SENDER, "Downgraded-Resent-Sender", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedResentTo, DOWNGRADED_RESENT_TO, "Downgraded-Resent-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedReturnPath, DOWNGRADED_RETURN_PATH, "Downgraded-Return-Path", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedSender, DOWNGRADED_SENDER, "Downgraded-Sender", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(DowngradedTo, DOWNGRADED_TO, "Downgraded-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5504, Standard::RFC6857]);
|
||||
(Encoding, ENCODING, "Encoding", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Encrypted, ENCRYPTED, "Encrypted", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Expires, EXPIRES, "Expires", None, Protocol::Mail | Protocol::NNTP, Status::None, &[Standard::RFC4021, Standard::RFC5536]);
|
||||
(ExpiryDate, EXPIRY_DATE, "Expiry-Date", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(FollowupTo, FOLLOWUP_TO, "Followup-To", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(From, FROM, "From", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5322, Standard::RFC6854]);
|
||||
(GenerateDeliveryReport, GENERATE_DELIVERY_REPORT, "Generate-Delivery-Report", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Importance, IMPORTANCE, "Importance", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(IncompleteCopy, INCOMPLETE_COPY, "Incomplete-Copy", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(InjectionDate, INJECTION_DATE, "Injection-Date", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(InjectionInfo, INJECTION_INFO, "Injection-Info", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(Language, LANGUAGE, "Language", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(LatestDeliveryTime, LATEST_DELIVERY_TIME, "Latest-Delivery-Time", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Lines, LINES, "Lines", None, Protocol::NNTP, Status::Deprecated, &[Standard::RFC5536, Standard::RFC3977]);
|
||||
(ListArchive, LIST_ARCHIVE, "List-Archive", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListHelp, LIST_HELP, "List-Help", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListId, LIST_ID, "List-ID", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListOwner, LIST_OWNER, "List-Owner", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListPost, LIST_POST, "List-Post", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListSubscribe, LIST_SUBSCRIBE, "List-Subscribe", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListUnsubscribe, LIST_UNSUBSCRIBE, "List-Unsubscribe", Some("perm/list-unsubscribe"), Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ListUnsubscribePost, LIST_UNSUBSCRIBE_POST, "List-Unsubscribe-Post", None, Protocol::Mail, Status::Standard, &[Standard::RFC8058]);
|
||||
(MessageContext, MESSAGE_CONTEXT, "Message-Context", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(MessageId, MESSAGE_ID, "Message-ID", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5322, Standard::RFC5536]);
|
||||
(MessageType, MESSAGE_TYPE, "Message-Type", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(MimeVersion, MIME_VERSION, "MIME-Version", None, Protocol::MIME, Status::None, &[Standard::RFC4021]);
|
||||
(MtPriority, MT_PRIORITY, "MT-Priority", None, Protocol::Mail, Status::Standard, &[Standard::RFC6758]);
|
||||
(Newsgroups, NEWSGROUPS, "Newsgroups", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(NntpPostingDate, NNTP_POSTING_DATE, "NNTP-Posting-Date", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC5536]);
|
||||
(NntpPostingHost, NNTP_POSTING_HOST, "NNTP-Posting-Host", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC2980, Standard::RFC5536]);
|
||||
(Obsoletes, OBSOLETES, "Obsoletes", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Organization, ORGANIZATION, "Organization", None, Protocol::Mail | Protocol::NNTP, Status::Informational, &[Standard::RFC7681, Standard::RFC5536]);
|
||||
(OriginalEncodedInformationTypes, ORIGINAL_ENCODED_INFORMATION_TYPES, "Original-Encoded-Information-Types", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(OriginalFrom, ORIGINAL_FROM, "Original-From", None, Protocol::Mail, Status::Standard, &[Standard::RFC5703]);
|
||||
(OriginalMessageId, ORIGINAL_MESSAGE_ID, "Original-Message-ID", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(OriginalRecipient, ORIGINAL_RECIPIENT, "Original-Recipient", Some("perm/original-recipient"),Protocol::Mail, Status::Standard, &[Standard::RFC3798, Standard::RFC5337]);
|
||||
(OriginalSender, ORIGINAL_SENDER, "Original-Sender", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5537]);
|
||||
(OriginatorReturnAddress, ORIGINATOR_RETURN_ADDRESS, "Originator-Return-Address", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(OriginalSubject, ORIGINAL_SUBJECT, "Original-Subject", None, Protocol::Mail, Status::Standard, &[Standard::RFC5703]);
|
||||
(Path, PATH, "Path", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(PicsLabel, PICS_LABEL, "PICS-Label", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(PostingVersion, POSTING_VERSION, "Posting-Version", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC0850, Standard::RFC5536]);
|
||||
(PreventNondeliveryReport, PREVENT_NONDELIVERY_REPORT, "Prevent-NonDelivery-Report", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Priority, PRIORITY, "Priority", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(ReceivedSpf, RECEIVED_SPF, "Received-SPF", None, Protocol::Mail, Status::Standard, &[Standard::RFC7208]);
|
||||
(RelayVersion, RELAY_VERSION, "Relay-Version", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC0850, Standard::RFC5536]);
|
||||
(ReplyBy, REPLY_BY, "Reply-By", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(RequireRecipientValidSince, REQUIRE_RECIPIENT_VALID_SINCE, "Require-Recipient-Valid-Since", None, Protocol::Mail, Status::Standard, &[Standard::RFC7293]);
|
||||
(ResentBcc, RESENT_BCC, "Resent-Bcc", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(ResentCc, RESENT_CC, "Resent-Cc", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(ResentDate, RESENT_DATE, "Resent-Date", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(ResentFrom, RESENT_FROM, "Resent-From", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322, Standard::RFC6854]);
|
||||
(ResentMessageId, RESENT_MESSAGE_ID, "Resent-Message-ID", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(ResentReplyTo, RESENT_REPLY_TO, "Resent-Reply-To", None, Protocol::Mail, Status::Obsoleted, &[Standard::RFC5322]);
|
||||
(ResentSender, RESENT_SENDER, "Resent-Sender", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322, Standard::RFC6854]);
|
||||
(ResentTo, RESENT_TO, "Resent-To", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(ReturnPath, RETURN_PATH, "Return-Path", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(SeeAlso, SEE_ALSO, "See-Also", None, Protocol::NNTP, Status::Obsoleted, &[Standard::RFC1849, Standard::RFC5536]);
|
||||
(Sender, SENDER, "Sender", None, Protocol::Mail | Protocol::NNTP, Status::Standard, &[Standard::RFC5322, Standard::RFC6854]);
|
||||
(Sensitivity, SENSITIVITY, "Sensitivity", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Solicitation, SOLICITATION, "Solicitation", None, Protocol::Mail, Status::None, &[Standard::RFC3865]);
|
||||
(Summary, SUMMARY, "Summary", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(Supersedes, SUPERSEDES, "Supersedes", None, Protocol::Mail | Protocol::NNTP, Status::None, &[Standard::RFC5536, Standard::RFC2156]);
|
||||
(TlsReportDomain, TLS_REPORT_DOMAIN, "TLS-Report-Domain", None, Protocol::Mail, Status::Standard, &[Standard::RFC8460]);
|
||||
(TlsReportSubmitter, TLS_REPORT_SUBMITTER, "TLS-Report-Submitter", None, Protocol::Mail, Status::Standard, &[Standard::RFC8460]);
|
||||
(TlsRequired, TLS_REQUIRED, "TLS-Required", None, Protocol::Mail, Status::Standard, &[Standard::RFC8689]);
|
||||
(To, TO, "To", None, Protocol::Mail, Status::Standard, &[Standard::RFC5322]);
|
||||
(UserAgent, USER_AGENT, "User-Agent", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536, Standard::RFC2616]);
|
||||
(VbrInfo, VBR_INFO, "VBR-Info", None, Protocol::Mail, Status::Standard, &[Standard::RFC5518]);
|
||||
(X400ContentIdentifier, X400_CONTENT_IDENTIFIER, "X400-Content-Identifier", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400ContentReturn, X400_CONTENT_RETURN, "X400-Content-Return", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400ContentType, X400_CONTENT_TYPE, "X400-Content-Type", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400MtsIdentifier, X400_MTS_IDENTIFIER, "X400-MTS-Identifier", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400Originator, X400_ORIGINATOR, "X400-Originator", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400Received, X400_RECEIVED, "X400-Received", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400Recipients, X400_RECIPIENTS, "X400-Recipients", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(X400Trace, X400_TRACE, "X400-Trace", None, Protocol::Mail, Status::None, &[Standard::RFC4021]);
|
||||
(Xref, XREF, "Xref", None, Protocol::NNTP, Status::Standard, &[Standard::RFC5536]);
|
||||
(ApparentlyTo, APPARENTLY_TO, "Apparently-To", Some("prov/apparently-to"), Protocol::Mail, Status::None, &[Standard::RFC2076]);
|
||||
(Author, AUTHOR, "Author", None, Protocol::Mail, Status::None, &[Standard::RFC9057]);
|
||||
(EdiintFeatures, EDIINT_FEATURES, "EDIINT-Features", None, Protocol::Mail, Status::None, &[Standard::RFC6017]);
|
||||
(EesstVersion, EESST_VERSION, "Eesst-Version", None, Protocol::Mail, Status::None, &[Standard::RFC7681]);
|
||||
(ErrorsTo, ERRORS_TO, "Errors-To", Some("prov/errors-to"), Protocol::Mail, Status::None, &[Standard::RFC2076]);
|
||||
(JabberId, JABBER_ID, "Jabber-ID", Some("prov/jabber-id"), Protocol::Mail | Protocol::NNTP, Status::None, &[Standard::RFC7259]);
|
||||
(SioLabel, SIO_LABEL, "SIO-Label", None, Protocol::Mail, Status::None, &[Standard::RFC7444]);
|
||||
(SioLabelHistory, SIO_LABEL_HISTORY, "SIO-Label-History", None, Protocol::Mail, Status::None, &[Standard::RFC7444]);
|
||||
(XArchivedAt, X_ARCHIVED_AT, "X-Archived-At", Some("prov/x-archived-at"), Protocol::Mail | Protocol::NNTP, Status::Deprecated, &[Standard::RFC5064]);
|
||||
(XMittente, X_MITTENTE, "X-Mittente", None, Protocol::Mail, Status::None, &[Standard::RFC6109]);
|
||||
(XRicevuta, X_RICEVUTA, "X-Ricevuta", None, Protocol::Mail, Status::None, &[Standard::RFC6109]);
|
||||
(XRiferimentoMessageId, X_RIFERIMENTO_MESSAGE_ID, "X-Riferimento-Message-ID", None, Protocol::Mail, Status::None, &[Standard::RFC6109]);
|
||||
(XTiporicevuta, X_TIPORICEVUTA, "X-TipoRicevuta", None, Protocol::Mail, Status::None, &[Standard::RFC6109]);
|
||||
(XTrasporto, X_TRASPORTO, "X-Trasporto", None, Protocol::Mail, Status::None, &[Standard::RFC6109]);
|
||||
(XVerificasicurezza, X_VERIFICASICUREZZA, "X-VerificaSicurezza", None, Protocol::Mail, Status::None, &[Standard::RFC6109]);
|
||||
}
|
||||
|
||||
/// Valid header name ASCII bytes
|
||||
///
|
||||
/// Source: [RFC5322 3.6.8.](https://datatracker.ietf.org/doc/html/rfc5322#autoid-35)
|
||||
/// ```text
|
||||
/// field-name = 1*ftext
|
||||
///
|
||||
/// ftext = %d33-57 / ; Printable US-ASCII
|
||||
/// %d59-126 ; characters not including
|
||||
/// ; ":".
|
||||
/// ```
|
||||
const HEADER_CHARS: [u8; 128] = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
|
||||
0, 0, 0, b'!', b'"', b'#', b'$', b'%', b'&', b'\'', // 3x
|
||||
0, 0, b'*', b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x
|
||||
b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', 0, 0, // 5x
|
||||
0, 0, 0, 0, 0, b'a', b'b', b'c', b'd', b'e', // 6x
|
||||
b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', // 7x
|
||||
b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', // 8x
|
||||
b'z', 0, 0, 0, b'^', b'_', b'`', b'a', b'b', b'c', // 9x
|
||||
b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x
|
||||
b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x
|
||||
b'x', b'y', b'z', 0, b'|', 0, b'~', 0, // 128
|
||||
];
|
||||
|
||||
impl HeaderName {
|
||||
/// Returns a `str` representation of the header.
|
||||
///
|
||||
/// The returned string will always be lower case.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self.inner {
|
||||
Repr::Standard(v) => v.as_str(),
|
||||
Repr::Custom(ref v) => unsafe { std::str::from_utf8_unchecked(&*v.0) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `&[u8]` representation of the header.
|
||||
///
|
||||
/// The returned slice will always be lower case.
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
match self.inner {
|
||||
Repr::Standard(v) => v.as_str().as_bytes(),
|
||||
Repr::Custom(ref v) => v.0.as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(src: &[u8]) -> Result<Self, InvalidHeaderName> {
|
||||
if let Some(std) = StandardHeader::from_bytes(src.trim()) {
|
||||
Ok(Self {
|
||||
inner: Repr::Standard(std),
|
||||
})
|
||||
} else {
|
||||
let mut buf = SmallVec::<[u8; 32]>::new();
|
||||
for b in src {
|
||||
if let Some(b) = HEADER_CHARS.get(*b as usize).filter(|b| **b != 0) {
|
||||
buf.push(*b);
|
||||
} else {
|
||||
return Err(InvalidHeaderName::new());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
inner: Repr::Custom(Custom(buf)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for HeaderName {
|
||||
type Err = InvalidHeaderName;
|
||||
|
||||
fn from_str(s: &str) -> Result<HeaderName, InvalidHeaderName> {
|
||||
HeaderName::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for HeaderName {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for HeaderName {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_str().as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for HeaderName {
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for HeaderName {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(fmt, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for HeaderName {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum Helper {
|
||||
S(String),
|
||||
B(Vec<u8>),
|
||||
}
|
||||
if let Ok(s) = <Helper>::deserialize(deserializer) {
|
||||
Self::from_bytes(match &s {
|
||||
Helper::S(v) => v.as_bytes(),
|
||||
Helper::B(v) => v.as_slice(),
|
||||
})
|
||||
.map_err(|_| de::Error::custom("invalid header name value"))
|
||||
} else {
|
||||
Err(de::Error::custom("invalid header name value"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for HeaderName {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl InvalidHeaderName {
|
||||
const fn new() -> InvalidHeaderName {
|
||||
InvalidHeaderName
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a HeaderName> for HeaderName {
|
||||
fn from(src: &'a HeaderName) -> Self {
|
||||
src.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&HeaderName> for Cow<'static, str> {
|
||||
fn from(src: &HeaderName) -> Self {
|
||||
match src.inner {
|
||||
Repr::Standard(s) => Cow::Borrowed(s.as_str()),
|
||||
Repr::Custom(_) => Cow::Owned(src.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for HeaderName {
|
||||
type Error = InvalidHeaderName;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for HeaderName {
|
||||
type Error = InvalidHeaderName;
|
||||
#[inline]
|
||||
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for HeaderName {
|
||||
type Error = InvalidHeaderName;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(s).map_err(|_| InvalidHeaderName::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for HeaderName {
|
||||
type Error = InvalidHeaderName;
|
||||
|
||||
#[inline]
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(s.as_bytes()).map_err(|_| InvalidHeaderName::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for HeaderName {
|
||||
type Error = InvalidHeaderName;
|
||||
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(&vec).map_err(|_| InvalidHeaderName::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<StandardHeader> for HeaderName {
|
||||
fn from(src: StandardHeader) -> HeaderName {
|
||||
HeaderName {
|
||||
inner: Repr::Standard(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<Custom> for HeaderName {
|
||||
fn from(src: Custom) -> HeaderName {
|
||||
HeaderName {
|
||||
inner: Repr::Custom(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a HeaderName> for HeaderName {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a HeaderName) -> bool {
|
||||
*self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<HeaderName> for &'a HeaderName {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderName) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for HeaderName {
|
||||
/// Performs a case-insensitive comparison of the string against the header
|
||||
/// name
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use melib::email::headers::HeaderName;
|
||||
///
|
||||
/// assert_eq!(HeaderName::CONTENT_LENGTH, "content-length");
|
||||
/// assert_eq!(HeaderName::CONTENT_LENGTH, "Content-Length");
|
||||
/// assert_ne!(HeaderName::CONTENT_LENGTH, "content length");
|
||||
/// ```
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str().eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderName> for str {
|
||||
/// Performs a case-insensitive comparison of the string against the header
|
||||
/// name
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// use melib::email::headers::HeaderName;
|
||||
///
|
||||
/// assert_eq!(HeaderName::CONTENT_LENGTH, "content-length");
|
||||
/// assert_eq!(HeaderName::CONTENT_LENGTH, "Content-Length");
|
||||
/// assert_ne!(HeaderName::CONTENT_LENGTH, "content length");
|
||||
/// assert_eq!(
|
||||
/// HeaderName::CONTENT_LENGTH,
|
||||
/// HeaderName::try_from("content-length").unwrap()
|
||||
/// );
|
||||
/// ```
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderName) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for HeaderName {
|
||||
/// Performs a case-insensitive comparison of the string against the header
|
||||
/// name
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
*self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<HeaderName> for &'a str {
|
||||
/// Performs a case-insensitive comparison of the string against the header
|
||||
/// name
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderName) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Custom {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
for b in self.0.as_slice() {
|
||||
hasher.write_u8(b.to_ascii_lowercase())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,11 +43,11 @@ impl From<Mailto> for Draft {
|
|||
bcc,
|
||||
body,
|
||||
} = val;
|
||||
ret.set_header("Subject", subject.unwrap_or_default());
|
||||
ret.set_header("Cc", cc.unwrap_or_default());
|
||||
ret.set_header("Bcc", bcc.unwrap_or_default());
|
||||
ret.set_header(HeaderName::SUBJECT, subject.unwrap_or_default());
|
||||
ret.set_header(HeaderName::CC, cc.unwrap_or_default());
|
||||
ret.set_header(HeaderName::BCC, bcc.unwrap_or_default());
|
||||
ret.set_header(HeaderName::TO, address.to_string());
|
||||
ret.set_body(body.unwrap_or_default());
|
||||
ret.set_header("To", address.to_string());
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
|
|
@ -306,6 +306,7 @@ impl From<isahc::http::StatusCode> for NetworkErrorKind {
|
|||
}
|
||||
|
||||
#[derive(Debug, Copy, PartialEq, Eq, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum ErrorKind {
|
||||
None,
|
||||
External,
|
||||
|
@ -317,6 +318,7 @@ pub enum ErrorKind {
|
|||
OSError,
|
||||
NotImplemented,
|
||||
NotSupported,
|
||||
ValueError,
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorKind {
|
||||
|
@ -325,16 +327,17 @@ impl fmt::Display for ErrorKind {
|
|||
fmt,
|
||||
"{}",
|
||||
match self {
|
||||
ErrorKind::None => "None",
|
||||
ErrorKind::External => "External",
|
||||
ErrorKind::Authentication => "Authentication",
|
||||
ErrorKind::Bug => "Bug, please report this!",
|
||||
ErrorKind::Network(ref inner) => inner.as_str(),
|
||||
ErrorKind::Timeout => "Timeout",
|
||||
ErrorKind::OSError => "OS Error",
|
||||
ErrorKind::Configuration => "Configuration",
|
||||
ErrorKind::NotImplemented => "Not implemented",
|
||||
ErrorKind::NotSupported => "Not supported",
|
||||
Self::None => "None",
|
||||
Self::External => "External",
|
||||
Self::Authentication => "Authentication",
|
||||
Self::Bug => "Bug, please report this!",
|
||||
Self::Network(ref inner) => inner.as_str(),
|
||||
Self::Timeout => "Timeout",
|
||||
Self::OSError => "OS Error",
|
||||
Self::Configuration => "Configuration",
|
||||
Self::NotImplemented => "Not implemented",
|
||||
Self::NotSupported => "Not supported",
|
||||
Self::ValueError => "Invalid value",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -342,15 +345,15 @@ impl fmt::Display for ErrorKind {
|
|||
|
||||
impl ErrorKind {
|
||||
pub fn is_network(&self) -> bool {
|
||||
matches!(self, ErrorKind::Network(_))
|
||||
matches!(self, Self::Network(_))
|
||||
}
|
||||
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
matches!(self, ErrorKind::Timeout)
|
||||
matches!(self, Self::Timeout)
|
||||
}
|
||||
|
||||
pub fn is_authentication(&self) -> bool {
|
||||
matches!(self, ErrorKind::Authentication)
|
||||
matches!(self, Self::Authentication)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,6 +694,15 @@ impl From<nom::Err<(&str, nom::error::ErrorKind)>> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<crate::email::InvalidHeaderName> for Error {
|
||||
#[inline]
|
||||
fn from(kind: crate::email::InvalidHeaderName) -> Error {
|
||||
Error::new(kind.to_string())
|
||||
.set_source(Some(Arc::new(kind)))
|
||||
.set_kind(ErrorKind::Network(NetworkErrorKind::InvalidTLSConnection))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut Error> for Error {
|
||||
#[inline]
|
||||
fn from(kind: &'a mut Error) -> Error {
|
||||
|
|
|
@ -141,7 +141,7 @@ impl fmt::Display for Composer {
|
|||
write!(
|
||||
f,
|
||||
"reply: {}",
|
||||
(&self.draft.headers()["Subject"]).trim_at_boundary(8)
|
||||
(&self.draft.headers()[HeaderName::SUBJECT]).trim_at_boundary(8)
|
||||
)
|
||||
} else {
|
||||
write!(f, "composing")
|
||||
|
@ -204,11 +204,11 @@ impl Composer {
|
|||
if v.is_empty() {
|
||||
continue;
|
||||
}
|
||||
ret.draft.set_header(h, v.into());
|
||||
ret.draft.set_header(h.into(), v.into());
|
||||
}
|
||||
if *account_settings!(context[account_hash].composing.insert_user_agent) {
|
||||
ret.draft.set_header(
|
||||
"User-Agent",
|
||||
HeaderName::USER_AGENT,
|
||||
format!("meli {}", option_env!("CARGO_PKG_VERSION").unwrap_or("0.0")),
|
||||
);
|
||||
}
|
||||
|
@ -296,9 +296,9 @@ impl Composer {
|
|||
subject.to_string()
|
||||
}
|
||||
};
|
||||
ret.draft.set_header("Subject", subject);
|
||||
ret.draft.set_header(HeaderName::SUBJECT, subject);
|
||||
ret.draft.set_header(
|
||||
"References",
|
||||
HeaderName::REFERENCES,
|
||||
format!(
|
||||
"{} {}",
|
||||
envelope
|
||||
|
@ -314,17 +314,19 @@ impl Composer {
|
|||
envelope.message_id_display()
|
||||
),
|
||||
);
|
||||
ret.draft
|
||||
.set_header("In-Reply-To", envelope.message_id_display().into());
|
||||
ret.draft.set_header(
|
||||
HeaderName::IN_REPLY_TO,
|
||||
envelope.message_id_display().into(),
|
||||
);
|
||||
|
||||
if let Some(reply_to) = envelope.other_headers().get("To") {
|
||||
if let Some(reply_to) = envelope.other_headers().get(HeaderName::TO) {
|
||||
let to: &str = reply_to;
|
||||
let extra_identities = &account.settings.account.extra_identities;
|
||||
if let Some(extra) = extra_identities
|
||||
.iter()
|
||||
.find(|extra| to.contains(extra.as_str()))
|
||||
{
|
||||
ret.draft.set_header("From", extra.into());
|
||||
ret.draft.set_header(HeaderName::FROM, extra.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,13 +350,13 @@ impl Composer {
|
|||
}
|
||||
if let Some(reply_to) = envelope
|
||||
.other_headers()
|
||||
.get("Mail-Followup-To")
|
||||
.get(HeaderName::MAIL_FOLLOWUP_TO)
|
||||
.and_then(|v| v.try_into().ok())
|
||||
{
|
||||
to.insert(reply_to);
|
||||
} else if let Some(reply_to) = envelope
|
||||
.other_headers()
|
||||
.get("Reply-To")
|
||||
.get(HeaderName::REPLY_TO)
|
||||
.and_then(|v| v.try_into().ok())
|
||||
{
|
||||
to.insert(reply_to);
|
||||
|
@ -371,7 +373,7 @@ impl Composer {
|
|||
) {
|
||||
to.remove(&ours);
|
||||
}
|
||||
ret.draft.set_header("To", {
|
||||
ret.draft.set_header(HeaderName::TO, {
|
||||
let mut ret: String =
|
||||
to.into_iter()
|
||||
.fold(String::new(), |mut s: String, n: Address| {
|
||||
|
@ -383,13 +385,15 @@ impl Composer {
|
|||
ret.pop();
|
||||
ret
|
||||
});
|
||||
ret.draft.set_header("Cc", envelope.field_cc_to_string());
|
||||
} else if let Some(reply_to) = envelope.other_headers().get("Mail-Reply-To") {
|
||||
ret.draft.set_header("To", reply_to.to_string());
|
||||
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
|
||||
ret.draft.set_header("To", reply_to.to_string());
|
||||
ret.draft
|
||||
.set_header(HeaderName::CC, envelope.field_cc_to_string());
|
||||
} else if let Some(reply_to) = envelope.other_headers().get(HeaderName::MAIL_REPLY_TO) {
|
||||
ret.draft.set_header(HeaderName::TO, reply_to.to_string());
|
||||
} else if let Some(reply_to) = envelope.other_headers().get(HeaderName::REPLY_TO) {
|
||||
ret.draft.set_header(HeaderName::TO, reply_to.to_string());
|
||||
} else {
|
||||
ret.draft.set_header("To", envelope.field_from_to_string());
|
||||
ret.draft
|
||||
.set_header(HeaderName::TO, envelope.field_from_to_string());
|
||||
}
|
||||
ret.draft.body = {
|
||||
let mut ret = attribution_string(
|
||||
|
@ -495,7 +499,7 @@ impl Composer {
|
|||
) -> Self {
|
||||
let mut composer = Composer::with_account(coordinates.0, context);
|
||||
let mut draft: Draft = Draft::default();
|
||||
draft.set_header("Subject", format!("Fwd: {}", env.subject()));
|
||||
draft.set_header(HeaderName::SUBJECT, format!("Fwd: {}", env.subject()));
|
||||
let preamble = format!(
|
||||
r#"
|
||||
---------- Forwarded message ---------
|
||||
|
@ -557,8 +561,15 @@ To: {}
|
|||
self.form.set_cursor(old_cursor);
|
||||
let headers = self.draft.headers();
|
||||
let account_hash = self.account_hash;
|
||||
for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] {
|
||||
if k == "To" || k == "Cc" || k == "Bcc" {
|
||||
for k in &[
|
||||
HeaderName::DATE,
|
||||
HeaderName::FROM,
|
||||
HeaderName::TO,
|
||||
HeaderName::CC,
|
||||
HeaderName::BCC,
|
||||
HeaderName::SUBJECT,
|
||||
] {
|
||||
if k == HeaderName::TO || k == HeaderName::CC || k == HeaderName::BCC {
|
||||
self.form.push_cl((
|
||||
k.into(),
|
||||
headers[k].to_string(),
|
||||
|
@ -571,7 +582,7 @@ To: {}
|
|||
.collect::<Vec<AutoCompleteEntry>>()
|
||||
}),
|
||||
));
|
||||
} else if k == "From" {
|
||||
} else if k == HeaderName::FROM {
|
||||
self.form.push_cl((
|
||||
k.into(),
|
||||
headers[k].to_string(),
|
||||
|
@ -812,10 +823,11 @@ impl Component for Composer {
|
|||
context[self.account_hash].pgp.auto_sign
|
||||
));
|
||||
}
|
||||
if !self.draft.headers().contains_key("From") || self.draft.headers()["From"].is_empty()
|
||||
if !self.draft.headers().contains_key(HeaderName::FROM)
|
||||
|| self.draft.headers()[HeaderName::FROM].is_empty()
|
||||
{
|
||||
self.draft.set_header(
|
||||
"From",
|
||||
HeaderName::FROM,
|
||||
context.accounts[&self.account_hash]
|
||||
.settings
|
||||
.account()
|
||||
|
@ -1230,7 +1242,8 @@ impl Component for Composer {
|
|||
UIEvent::FinishedUIDialog(id, ref mut result),
|
||||
) if selector.id() == *id => {
|
||||
if let Some(to_val) = result.downcast_mut::<String>() {
|
||||
self.draft.set_header("To", std::mem::take(to_val));
|
||||
self.draft
|
||||
.set_header(HeaderName::TO, std::mem::take(to_val));
|
||||
self.update_form();
|
||||
}
|
||||
self.mode = ViewMode::Edit;
|
||||
|
@ -1381,28 +1394,6 @@ impl Component for Composer {
|
|||
UIEvent::Resize => {
|
||||
self.set_dirty(true);
|
||||
}
|
||||
/*
|
||||
/* Switch e-mail From: field to the `left` configured account. */
|
||||
UIEvent::Input(Key::Left) if self.cursor == Cursor::From => {
|
||||
self.draft.headers_mut().insert(
|
||||
"From".into(),
|
||||
get_display_name(context, self.account_hash),
|
||||
);
|
||||
self.dirty = true;
|
||||
return true;
|
||||
}
|
||||
/* Switch e-mail From: field to the `right` configured account. */
|
||||
UIEvent::Input(Key::Right) if self.cursor == Cursor::From => {
|
||||
if self.account_cursor + 1 < context.accounts.len() {
|
||||
self.account_cursor += 1;
|
||||
self.draft.headers_mut().insert(
|
||||
"From".into(),
|
||||
get_display_name(context, self.account_cursor),
|
||||
);
|
||||
self.dirty = true;
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
UIEvent::Input(ref key)
|
||||
if self.mode.is_edit()
|
||||
&& shortcut!(key == shortcuts[Shortcuts::COMPOSING]["scroll_up"]) =>
|
||||
|
@ -2494,9 +2485,12 @@ hello world.
|
|||
&mut context,
|
||||
false,
|
||||
);
|
||||
assert_eq!(&composer.draft.headers()["Subject"], "RE: your e-mail");
|
||||
assert_eq!(
|
||||
&composer.draft.headers()["To"],
|
||||
&composer.draft.headers()[HeaderName::SUBJECT],
|
||||
"RE: your e-mail"
|
||||
);
|
||||
assert_eq!(
|
||||
&composer.draft.headers()[HeaderName::TO],
|
||||
r#"some name <some@example.com>"#
|
||||
);
|
||||
let raw_mail = r#"From: "some name" <some@example.com>
|
||||
|
@ -2520,9 +2514,12 @@ hello world.
|
|||
&mut context,
|
||||
false,
|
||||
);
|
||||
assert_eq!(&composer.draft.headers()["Subject"], "Re: your e-mail");
|
||||
assert_eq!(
|
||||
&composer.draft.headers()["To"],
|
||||
&composer.draft.headers()[HeaderName::SUBJECT],
|
||||
"Re: your e-mail"
|
||||
);
|
||||
assert_eq!(
|
||||
&composer.draft.headers()[HeaderName::TO],
|
||||
r#"some name <some@example.com>"#
|
||||
);
|
||||
}
|
||||
|
|
|
@ -275,8 +275,8 @@ mod tests {
|
|||
let mut draft = Draft::default();
|
||||
draft
|
||||
.set_body("αδφαφσαφασ".to_string())
|
||||
.set_header("Subject", "test_update()".into())
|
||||
.set_header("Date", "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
.set_header(HeaderName::SUBJECT, "test_update()".into())
|
||||
.set_header(HeaderName::DATE, "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
println!("Check that past Date header value produces a warning…");
|
||||
#[allow(const_item_mutation)]
|
||||
let err_msg = PASTDATEWARN(&mut ctx, &mut draft).unwrap_err().to_string();
|
||||
|
@ -299,8 +299,8 @@ mod tests {
|
|||
let mut draft = Draft::default();
|
||||
draft
|
||||
.set_body("αδφαφσαφασ".to_string())
|
||||
.set_header("Subject", "test_update()".into())
|
||||
.set_header("Date", "Sun sds16 Jun 2013 17:56:45 +0200".into());
|
||||
.set_header(HeaderName::SUBJECT, "test_update()".into())
|
||||
.set_header(HeaderName::DATE, "Sun sds16 Jun 2013 17:56:45 +0200".into());
|
||||
let mut hook = HEADERWARN;
|
||||
|
||||
println!("Check for missing/empty From header value…");
|
||||
|
@ -312,7 +312,7 @@ mod tests {
|
|||
"HEADERWARN should complain about From value being empty: {}",
|
||||
err_msg
|
||||
);
|
||||
draft.set_header("From", "user <user@example.com>".into());
|
||||
draft.set_header(HeaderName::FROM, "user <user@example.com>".into());
|
||||
|
||||
println!("Check for missing/empty To header value…");
|
||||
let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string();
|
||||
|
@ -323,7 +323,7 @@ mod tests {
|
|||
"HEADERWARN should complain about To value being empty: {}",
|
||||
err_msg
|
||||
);
|
||||
draft.set_header("To", "other user <user@example.com>".into());
|
||||
draft.set_header(HeaderName::TO, "other user <user@example.com>".into());
|
||||
|
||||
println!("Check for invalid Date header value…");
|
||||
let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string();
|
||||
|
@ -337,11 +337,11 @@ mod tests {
|
|||
draft = Draft::default();
|
||||
draft
|
||||
.set_body("αδφαφσαφασ".to_string())
|
||||
.set_header("From", "user <user@example.com>".into())
|
||||
.set_header("To", "other user <user@example.com>".into())
|
||||
.set_header("Subject", "test_update()".into());
|
||||
.set_header(HeaderName::FROM, "user <user@example.com>".into())
|
||||
.set_header(HeaderName::TO, "other user <user@example.com>".into())
|
||||
.set_header(HeaderName::SUBJECT, "test_update()".into());
|
||||
hook(&mut ctx, &mut draft).unwrap();
|
||||
draft.set_header("From", "user user@example.com>".into());
|
||||
draft.set_header(HeaderName::FROM, "user user@example.com>".into());
|
||||
|
||||
println!("Check for invalid From header value…");
|
||||
let err_msg = hook(&mut ctx, &mut draft).unwrap_err().to_string();
|
||||
|
@ -361,8 +361,8 @@ mod tests {
|
|||
let mut draft = Draft::default();
|
||||
draft
|
||||
.set_body("αδφαφσαφασ".to_string())
|
||||
.set_header("Subject", "Attachments included".into())
|
||||
.set_header("Date", "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
.set_header(HeaderName::SUBJECT, "Attachments included".into())
|
||||
.set_header(HeaderName::DATE, "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
|
||||
let mut hook = MISSINGATTACHMENTWARN;
|
||||
|
||||
|
@ -378,7 +378,7 @@ mod tests {
|
|||
);
|
||||
|
||||
draft
|
||||
.set_header("Subject", "Hello.".into())
|
||||
.set_header(HeaderName::SUBJECT, "Hello.".into())
|
||||
.set_body("Attachments included".to_string());
|
||||
println!(
|
||||
"Check that mentioning attachments in body produces a warning if draft has no \
|
||||
|
@ -394,7 +394,7 @@ mod tests {
|
|||
println!(
|
||||
"Check that mentioning attachments produces no warnings if draft has attachments…"
|
||||
);
|
||||
draft.set_header("Subject", "Attachments included".into());
|
||||
draft.set_header(HeaderName::SUBJECT, "Attachments included".into());
|
||||
let mut attachment = AttachmentBuilder::new(b"");
|
||||
attachment
|
||||
.set_raw(b"foobar".to_vec())
|
||||
|
@ -414,7 +414,7 @@ mod tests {
|
|||
let tempdir = tempfile::tempdir().unwrap();
|
||||
let mut ctx = Context::new_mock(&tempdir);
|
||||
let mut draft = Draft::default();
|
||||
draft.set_header("Date", "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
draft.set_header(HeaderName::DATE, "Sun, 16 Jun 2013 17:56:45 +0200".into());
|
||||
|
||||
let mut hook = EMPTYDRAFTWARN;
|
||||
|
||||
|
@ -427,7 +427,7 @@ mod tests {
|
|||
);
|
||||
|
||||
println!("Check that non-empty draft produces no warning…");
|
||||
draft.set_header("Subject", "Ping".into());
|
||||
draft.set_header(HeaderName::SUBJECT, "Ping".into());
|
||||
hook(&mut ctx, &mut draft).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2663,7 +2663,7 @@ impl Component for MailView {
|
|||
if let Ok(mailto) = Mailto::try_from(*email) {
|
||||
let mut draft: Draft = mailto.into();
|
||||
draft.set_header(
|
||||
"From",
|
||||
HeaderName::FROM,
|
||||
context.accounts[&self.coordinates.0]
|
||||
.settings
|
||||
.account()
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
//! Configuration for composing email.
|
||||
use std::collections::HashMap;
|
||||
|
||||
use melib::ToggleFlag;
|
||||
use melib::{email::HeaderName, ToggleFlag};
|
||||
|
||||
use super::{
|
||||
default_vals::{ask, false_val, none, true_val},
|
||||
|
@ -60,7 +60,7 @@ pub struct ComposingSettings {
|
|||
/// Set default header values for new drafts
|
||||
/// Default: empty
|
||||
#[serde(default, alias = "default-header-values")]
|
||||
pub default_header_values: HashMap<String, String>,
|
||||
pub default_header_values: HashMap<HeaderName, String>,
|
||||
/// Wrap header preample when editing a draft in an editor. This allows you
|
||||
/// to write non-plain text email without the preamble creating syntax
|
||||
/// errors. They are stripped when you return from the editor. The
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
#![allow(clippy::derivable_impls)]
|
||||
|
||||
//! This module is automatically generated by config_macros.rs.
|
||||
|
||||
use super::*;
|
||||
use melib::HeaderName;
|
||||
|
||||
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct PagerSettingsOverride { # [doc = " Number of context lines when going to next page."] # [doc = " Default: 0"] # [serde (alias = "pager-context")] # [serde (default)] pub pager_context : Option < usize > , # [doc = " Stop at the end instead of displaying next mail."] # [doc = " Default: false"] # [serde (alias = "pager-stop")] # [serde (default)] pub pager_stop : Option < bool > , # [doc = " Always show headers when scrolling."] # [doc = " Default: true"] # [serde (alias = "headers-sticky")] # [serde (default)] pub headers_sticky : Option < bool > , # [doc = " The height of the pager in mail view, in percent."] # [doc = " Default: 80"] # [serde (alias = "pager-ratio")] # [serde (default)] pub pager_ratio : Option < usize > , # [doc = " A command to pipe mail output through for viewing in pager."] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string")] # [serde (default)] pub filter : Option < Option < String > > , # [doc = " A command to pipe html output before displaying it in a pager"] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string" , alias = "html-filter")] # [serde (default)] pub html_filter : Option < Option < String > > , # [doc = " Respect \"format=flowed\""] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = " Split long lines that would overflow on the x axis."] # [doc = " Default: true"] # [serde (alias = "split-long-lines")] # [serde (default)] pub split_long_lines : Option < bool > , # [doc = " Minimum text width in columns."] # [doc = " Default: 80"] # [serde (alias = "minimum-width")] # [serde (default)] pub minimum_width : Option < usize > , # [doc = " Choose `text/html` alternative if `text/plain` is empty in"] # [doc = " `multipart/alternative` attachments."] # [doc = " Default: true"] # [serde (alias = "auto-choose-multipart-alternative")] # [serde (default)] pub auto_choose_multipart_alternative : Option < ToggleFlag > , # [doc = " Show Date: in my timezone"] # [doc = " Default: true"] # [serde (alias = "show-date-in-my-timezone")] # [serde (default)] pub show_date_in_my_timezone : Option < ToggleFlag > , # [doc = " A command to launch URLs with. The URL will be given as the first"] # [doc = " argument of the command. Default: None"] # [serde (deserialize_with = "non_empty_opt_string")] # [serde (default)] pub url_launcher : Option < Option < String > > , # [doc = " A command to open html files."] # [doc = " Default: None"] # [serde (deserialize_with = "non_empty_opt_string" , alias = "html-open")] # [serde (default)] pub html_open : Option < Option < String > > } impl Default for PagerSettingsOverride { fn default () -> Self { PagerSettingsOverride { pager_context : None , pager_stop : None , headers_sticky : None , pager_ratio : None , filter : None , html_filter : None , format_flowed : None , split_long_lines : None , minimum_width : None , auto_choose_multipart_alternative : None , show_date_in_my_timezone : None , url_launcher : None , html_open : None } } }
|
||||
|
||||
|
@ -33,7 +35,7 @@ use super::*;
|
|||
|
||||
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ShortcutsOverride { # [serde (default)] pub general : Option < GeneralShortcuts > , # [serde (default)] pub listing : Option < ListingShortcuts > , # [serde (default)] pub composing : Option < ComposingShortcuts > , # [serde (alias = "contact-list")] # [serde (default)] pub contact_list : Option < ContactListShortcuts > , # [serde (alias = "envelope-view")] # [serde (default)] pub envelope_view : Option < EnvelopeViewShortcuts > , # [serde (alias = "thread-view")] # [serde (default)] pub thread_view : Option < ThreadViewShortcuts > , # [serde (default)] pub pager : Option < PagerShortcuts > } impl Default for ShortcutsOverride { fn default () -> Self { ShortcutsOverride { general : None , listing : None , composing : None , contact_list : None , envelope_view : None , thread_view : None , pager : None } } }
|
||||
|
||||
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ComposingSettingsOverride { # [doc = " A command to pipe new emails to"] # [doc = " Required"] # [serde (default)] pub send_mail : Option < SendMail > , # [doc = " Command to launch editor. Can have arguments. Draft filename is given as"] # [doc = " the last argument. If it's missing, the environment variable $EDITOR is"] # [doc = " looked up."] # [serde (alias = "editor-command" , alias = "editor-cmd" , alias = "editor_cmd")] # [serde (default)] pub editor_command : Option < Option < String > > , # [doc = " Embed editor (for terminal interfaces) instead of forking and waiting."] # [serde (default)] pub embed : Option < bool > , # [doc = " Set \"format=flowed\" in plain text attachments."] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = "Set User-Agent"] # [doc = "Default: empty"] # [serde (alias = "insert_user_agent")] # [serde (default)] pub insert_user_agent : Option < bool > , # [doc = " Set default header values for new drafts"] # [doc = " Default: empty"] # [serde (alias = "default-header-values")] # [serde (default)] pub default_header_values : Option < HashMap < String , String > > , # [doc = " Wrap header preample when editing a draft in an editor. This allows you"] # [doc = " to write non-plain text email without the preamble creating syntax"] # [doc = " errors. They are stripped when you return from the editor. The"] # [doc = " values should be a two element array of strings, a prefix and suffix."] # [doc = " Default: None"] # [serde (alias = "wrap-header-preample")] # [serde (default)] pub wrap_header_preamble : Option < Option < (String , String) > > , # [doc = " Store sent mail after successful submission. This setting is meant to be"] # [doc = " disabled for non-standard behaviour in gmail, which auto-saves sent"] # [doc = " mail on its own. Default: true"] # [serde (default)] pub store_sent_mail : Option < bool > , # [doc = " The attribution line appears above the quoted reply text."] # [doc = " The format specifiers for the replied address are:"] # [doc = " - `%+f` — the sender's name and email address."] # [doc = " - `%+n` — the sender's name (or email address, if no name is included)."] # [doc = " - `%+a` — the sender's email address."] # [doc = " The format string is passed to strftime(3) with the replied envelope's"] # [doc = " date. Default: \"On %a, %0e %b %Y %H:%M, %+f wrote:%n\""] # [serde (default)] pub attribution_format_string : Option < Option < String > > , # [doc = " Whether the strftime call for the attribution string uses the POSIX"] # [doc = " locale instead of the user's active locale"] # [doc = " Default: true"] # [serde (default)] pub attribution_use_posix_locale : Option < bool > , # [doc = " Forward emails as attachment? (Alternative is inline)"] # [doc = " Default: ask"] # [serde (alias = "forward-as-attachment")] # [serde (default)] pub forward_as_attachment : Option < ToggleFlag > , # [doc = " Alternative lists of reply prefixes (etc. [\"Re:\", \"RE:\", ...]) to strip"] # [doc = " Default: `[\"Re:\", \"RE:\", \"Fwd:\", \"Fw:\", \"回复:\", \"回覆:\", \"SV:\", \"Sv:\","] # [doc = " \"VS:\", \"Antw:\", \"Doorst:\", \"VS:\", \"VL:\", \"REF:\", \"TR:\", \"TR:\", \"AW:\","] # [doc = " \"WG:\", \"ΑΠ:\", \"Απ:\", \"απ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"ΣΧΕΤ:\", \"Σχετ:\","] # [doc = " \"σχετ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"Vá:\", \"Továbbítás:\", \"R:\", \"I:\","] # [doc = " \"RIF:\", \"FS:\", \"BLS:\", \"TRS:\", \"VS:\", \"VB:\", \"RV:\", \"RES:\", \"Res\","] # [doc = " \"ENC:\", \"Odp:\", \"PD:\", \"YNT:\", \"İLT:\", \"ATB:\", \"YML:\"]`"] # [serde (alias = "reply-prefix-list-to-strip")] # [serde (default)] pub reply_prefix_list_to_strip : Option < Option < Vec < String > > > , # [doc = " The prefix to use in reply subjects. The de facto prefix is \"Re:\"."] # [serde (alias = "reply-prefix")] # [serde (default)] pub reply_prefix : Option < String > , # [doc = " Custom `compose-hooks`."] # [serde (alias = "custom-compose-hooks")] # [serde (default)] pub custom_compose_hooks : Option < Vec < ComposeHook > > , # [doc = " Disabled `compose-hooks`."] # [serde (alias = "disabled-compose-hooks")] # [serde (default)] pub disabled_compose_hooks : Option < Vec < String > > } impl Default for ComposingSettingsOverride { fn default () -> Self { ComposingSettingsOverride { send_mail : None , editor_command : None , embed : None , format_flowed : None , insert_user_agent : None , default_header_values : None , wrap_header_preamble : None , store_sent_mail : None , attribution_format_string : None , attribution_use_posix_locale : None , forward_as_attachment : None , reply_prefix_list_to_strip : None , reply_prefix : None , custom_compose_hooks : None , disabled_compose_hooks : None } } }
|
||||
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct ComposingSettingsOverride { # [doc = " A command to pipe new emails to"] # [doc = " Required"] # [serde (default)] pub send_mail : Option < SendMail > , # [doc = " Command to launch editor. Can have arguments. Draft filename is given as"] # [doc = " the last argument. If it's missing, the environment variable $EDITOR is"] # [doc = " looked up."] # [serde (alias = "editor-command" , alias = "editor-cmd" , alias = "editor_cmd")] # [serde (default)] pub editor_command : Option < Option < String > > , # [doc = " Embed editor (for terminal interfaces) instead of forking and waiting."] # [serde (default)] pub embed : Option < bool > , # [doc = " Set \"format=flowed\" in plain text attachments."] # [doc = " Default: true"] # [serde (alias = "format-flowed")] # [serde (default)] pub format_flowed : Option < bool > , # [doc = "Set User-Agent"] # [doc = "Default: empty"] # [serde (alias = "insert_user_agent")] # [serde (default)] pub insert_user_agent : Option < bool > , # [doc = " Set default header values for new drafts"] # [doc = " Default: empty"] # [serde (alias = "default-header-values")] # [serde (default)] pub default_header_values : Option < HashMap < HeaderName , String > > , # [doc = " Wrap header preample when editing a draft in an editor. This allows you"] # [doc = " to write non-plain text email without the preamble creating syntax"] # [doc = " errors. They are stripped when you return from the editor. The"] # [doc = " values should be a two element array of strings, a prefix and suffix."] # [doc = " Default: None"] # [serde (alias = "wrap-header-preample")] # [serde (default)] pub wrap_header_preamble : Option < Option < (String , String) > > , # [doc = " Store sent mail after successful submission. This setting is meant to be"] # [doc = " disabled for non-standard behaviour in gmail, which auto-saves sent"] # [doc = " mail on its own. Default: true"] # [serde (default)] pub store_sent_mail : Option < bool > , # [doc = " The attribution line appears above the quoted reply text."] # [doc = " The format specifiers for the replied address are:"] # [doc = " - `%+f` — the sender's name and email address."] # [doc = " - `%+n` — the sender's name (or email address, if no name is included)."] # [doc = " - `%+a` — the sender's email address."] # [doc = " The format string is passed to strftime(3) with the replied envelope's"] # [doc = " date. Default: \"On %a, %0e %b %Y %H:%M, %+f wrote:%n\""] # [serde (default)] pub attribution_format_string : Option < Option < String > > , # [doc = " Whether the strftime call for the attribution string uses the POSIX"] # [doc = " locale instead of the user's active locale"] # [doc = " Default: true"] # [serde (default)] pub attribution_use_posix_locale : Option < bool > , # [doc = " Forward emails as attachment? (Alternative is inline)"] # [doc = " Default: ask"] # [serde (alias = "forward-as-attachment")] # [serde (default)] pub forward_as_attachment : Option < ToggleFlag > , # [doc = " Alternative lists of reply prefixes (etc. [\"Re:\", \"RE:\", ...]) to strip"] # [doc = " Default: `[\"Re:\", \"RE:\", \"Fwd:\", \"Fw:\", \"回复:\", \"回覆:\", \"SV:\", \"Sv:\","] # [doc = " \"VS:\", \"Antw:\", \"Doorst:\", \"VS:\", \"VL:\", \"REF:\", \"TR:\", \"TR:\", \"AW:\","] # [doc = " \"WG:\", \"ΑΠ:\", \"Απ:\", \"απ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"ΣΧΕΤ:\", \"Σχετ:\","] # [doc = " \"σχετ:\", \"ΠΡΘ:\", \"Πρθ:\", \"πρθ:\", \"Vá:\", \"Továbbítás:\", \"R:\", \"I:\","] # [doc = " \"RIF:\", \"FS:\", \"BLS:\", \"TRS:\", \"VS:\", \"VB:\", \"RV:\", \"RES:\", \"Res\","] # [doc = " \"ENC:\", \"Odp:\", \"PD:\", \"YNT:\", \"İLT:\", \"ATB:\", \"YML:\"]`"] # [serde (alias = "reply-prefix-list-to-strip")] # [serde (default)] pub reply_prefix_list_to_strip : Option < Option < Vec < String > > > , # [doc = " The prefix to use in reply subjects. The de facto prefix is \"Re:\"."] # [serde (alias = "reply-prefix")] # [serde (default)] pub reply_prefix : Option < String > , # [doc = " Custom `compose-hooks`."] # [serde (alias = "custom-compose-hooks")] # [serde (default)] pub custom_compose_hooks : Option < Vec < ComposeHook > > , # [doc = " Disabled `compose-hooks`."] # [serde (alias = "disabled-compose-hooks")] # [serde (default)] pub disabled_compose_hooks : Option < Vec < String > > } impl Default for ComposingSettingsOverride { fn default () -> Self { ComposingSettingsOverride { send_mail : None , editor_command : None , embed : None , format_flowed : None , insert_user_agent : None , default_header_values : None , wrap_header_preamble : None , store_sent_mail : None , attribution_format_string : None , attribution_use_posix_locale : None , forward_as_attachment : None , reply_prefix_list_to_strip : None , reply_prefix : None , custom_compose_hooks : None , disabled_compose_hooks : None } } }
|
||||
|
||||
# [derive (Debug , Serialize , Deserialize , Clone)] # [serde (deny_unknown_fields)] pub struct TagsSettingsOverride { # [serde (deserialize_with = "tag_color_de")] # [serde (default)] pub colors : Option < HashMap < TagHash , Color > > , # [serde (deserialize_with = "tag_set_de" , alias = "ignore-tags")] # [serde (default)] pub ignore_tags : Option < HashSet < TagHash > > } impl Default for TagsSettingsOverride { fn default () -> Self { TagsSettingsOverride { colors : None , ignore_tags : None } } }
|
||||
|
||||
|
|
Loading…
Reference in New Issue