melib: turn unicode algos and backends into features
parent
f066f35410
commit
6e75160b70
|
@ -12,16 +12,24 @@ crossbeam = "0.7.2"
|
||||||
data-encoding = "2.1.1"
|
data-encoding = "2.1.1"
|
||||||
encoding = "0.2.33"
|
encoding = "0.2.33"
|
||||||
fnv = "1.0.3"
|
fnv = "1.0.3"
|
||||||
memmap = "0.5.2"
|
memmap = { version = "0.5.2", optional = true }
|
||||||
nom = "3.2.0"
|
nom = "3.2.0"
|
||||||
notify = "4.0.1"
|
notify = { version = "4.0.1", optional = true }
|
||||||
notify-rust = "^3"
|
notify-rust = { version = "^3", optional = true }
|
||||||
termion = "1.5.1"
|
termion = "1.5.1"
|
||||||
xdg = "2.1.0"
|
xdg = "2.1.0"
|
||||||
native-tls = "0.2"
|
native-tls = { version ="0.2", optional=true }
|
||||||
serde = "1.0.71"
|
serde = "1.0.71"
|
||||||
serde_derive = "1.0.71"
|
serde_derive = "1.0.71"
|
||||||
bincode = "1.0.1"
|
bincode = "1.0.1"
|
||||||
uuid = { version = "0.6", features = ["serde", "v4"] }
|
uuid = { version = "0.6", features = ["serde", "v4"] }
|
||||||
text_processing = { path = "../text_processing", version = "*" }
|
text_processing = { path = "../text_processing", version = "*", optional= true }
|
||||||
libc = {version = "0.2.59", features = ["extra_traits",]}
|
libc = {version = "0.2.59", features = ["extra_traits",]}
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["unicode_algorithms", "imap_backend", "maildir_backend", "mbox_backend"]
|
||||||
|
|
||||||
|
unicode_algorithms = ["text_processing"]
|
||||||
|
imap_backend = ["native-tls"]
|
||||||
|
maildir_backend = ["notify", "notify-rust", "memmap"]
|
||||||
|
mbox_backend = ["notify", "notify-rust", "memmap"]
|
||||||
|
|
|
@ -18,16 +18,22 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
#[cfg(feature = "imap_backend")]
|
||||||
pub mod imap;
|
pub mod imap;
|
||||||
|
#[cfg(feature = "maildir_backend")]
|
||||||
pub mod maildir;
|
pub mod maildir;
|
||||||
|
#[cfg(feature = "mbox_backend")]
|
||||||
pub mod mbox;
|
pub mod mbox;
|
||||||
|
|
||||||
|
#[cfg(feature = "imap_backend")]
|
||||||
pub use self::imap::ImapType;
|
pub use self::imap::ImapType;
|
||||||
use crate::async_workers::*;
|
use crate::async_workers::*;
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::error::{MeliError, Result};
|
use crate::error::{MeliError, Result};
|
||||||
//use mailbox::backends::imap::ImapType;
|
|
||||||
|
#[cfg(feature = "maildir_backend")]
|
||||||
use self::maildir::MaildirType;
|
use self::maildir::MaildirType;
|
||||||
|
#[cfg(feature = "mbox_backend")]
|
||||||
use self::mbox::MboxType;
|
use self::mbox::MboxType;
|
||||||
use super::email::{Envelope, EnvelopeHash, Flag};
|
use super::email::{Envelope, EnvelopeHash, Flag};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -57,18 +63,27 @@ impl Backends {
|
||||||
let mut b = Backends {
|
let mut b = Backends {
|
||||||
map: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
|
map: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
|
||||||
};
|
};
|
||||||
b.register(
|
#[cfg(feature = "maildir_backend")]
|
||||||
"maildir".to_string(),
|
{
|
||||||
Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
|
b.register(
|
||||||
);
|
"maildir".to_string(),
|
||||||
b.register(
|
Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
|
||||||
"mbox".to_string(),
|
);
|
||||||
Box::new(|| Box::new(|f, i| Box::new(MboxType::new(f, i)))),
|
}
|
||||||
);
|
#[cfg(feature = "mbox_backend")]
|
||||||
b.register(
|
{
|
||||||
"imap".to_string(),
|
b.register(
|
||||||
Box::new(|| Box::new(|f, i| Box::new(ImapType::new(f, i)))),
|
"mbox".to_string(),
|
||||||
);
|
Box::new(|| Box::new(|f, i| Box::new(MboxType::new(f, i)))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "imap_backend")]
|
||||||
|
{
|
||||||
|
b.register(
|
||||||
|
"imap".to_string(),
|
||||||
|
Box::new(|| Box::new(|f, i| Box::new(ImapType::new(f, i)))),
|
||||||
|
);
|
||||||
|
}
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,51 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
use text_processing::grapheme_clusters::Graphemes;
|
use text_processing::grapheme_clusters::Graphemes;
|
||||||
|
|
||||||
pub fn encode_header(value: &str) -> String {
|
pub fn encode_header(value: &str) -> String {
|
||||||
let mut ret = String::with_capacity(value.len());
|
let mut ret = String::with_capacity(value.len());
|
||||||
let graphemes = value.graphemes_indices();
|
|
||||||
let mut is_current_window_ascii = true;
|
let mut is_current_window_ascii = true;
|
||||||
let mut current_window_start = 0;
|
let mut current_window_start = 0;
|
||||||
for (idx, g) in graphemes {
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
match (g.is_ascii(), is_current_window_ascii) {
|
{
|
||||||
(true, true) => {
|
let graphemes = value.graphemes_indices();
|
||||||
ret.push_str(g);
|
for (idx, g) in graphemes {
|
||||||
}
|
match (g.is_ascii(), is_current_window_ascii) {
|
||||||
(true, false) => {
|
(true, true) => {
|
||||||
/* If !g.is_whitespace()
|
ret.push_str(g);
|
||||||
*
|
}
|
||||||
* Whitespaces inside encoded tokens must be greedily taken,
|
(true, false) => {
|
||||||
* instead of splitting each non-ascii word into separate encoded tokens. */
|
/* If !g.is_whitespace()
|
||||||
if !g.split_whitespace().collect::<Vec<&str>>().is_empty() {
|
*
|
||||||
|
* Whitespaces inside encoded tokens must be greedily taken,
|
||||||
|
* instead of splitting each non-ascii word into separate encoded tokens. */
|
||||||
|
if !g.split_whitespace().collect::<Vec<&str>>().is_empty() {
|
||||||
|
ret.push_str(&format!(
|
||||||
|
"=?UTF-8?B?{}?=",
|
||||||
|
BASE64_MIME
|
||||||
|
.encode(value[current_window_start..idx].as_bytes())
|
||||||
|
.trim()
|
||||||
|
));
|
||||||
|
if idx != value.len() - 1 {
|
||||||
|
ret.push(' ');
|
||||||
|
}
|
||||||
|
is_current_window_ascii = true;
|
||||||
|
current_window_start = idx;
|
||||||
|
ret.push_str(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
current_window_start = idx;
|
||||||
|
is_current_window_ascii = false;
|
||||||
|
}
|
||||||
|
/* RFC2047 recommends:
|
||||||
|
* 'While there is no limit to the length of a multiple-line header field, each line of
|
||||||
|
* a header field that contains one or more 'encoded-word's is limited to 76
|
||||||
|
* characters.'
|
||||||
|
* This is a rough compliance.
|
||||||
|
*/
|
||||||
|
(false, false) if (((4 * (idx - current_window_start) / 3) + 3) & !3) > 33 => {
|
||||||
ret.push_str(&format!(
|
ret.push_str(&format!(
|
||||||
"=?UTF-8?B?{}?=",
|
"=?UTF-8?B?{}?=",
|
||||||
BASE64_MIME
|
BASE64_MIME
|
||||||
|
@ -26,34 +55,67 @@ pub fn encode_header(value: &str) -> String {
|
||||||
if idx != value.len() - 1 {
|
if idx != value.len() - 1 {
|
||||||
ret.push(' ');
|
ret.push(' ');
|
||||||
}
|
}
|
||||||
is_current_window_ascii = true;
|
|
||||||
current_window_start = idx;
|
current_window_start = idx;
|
||||||
ret.push_str(g);
|
|
||||||
}
|
}
|
||||||
|
(false, false) => {}
|
||||||
}
|
}
|
||||||
(false, true) => {
|
}
|
||||||
current_window_start = idx;
|
}
|
||||||
is_current_window_ascii = false;
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
}
|
{
|
||||||
/* RFC2047 recommends:
|
/* TODO: test this. If it works as fine as the one above, there's no need to keep the above
|
||||||
* 'While there is no limit to the length of a multiple-line header field, each line of
|
* implementation.*/
|
||||||
* a header field that contains one or more 'encoded-word's is limited to 76
|
let mut idx = 0;
|
||||||
* characters.'
|
for g in value.chars() {
|
||||||
* This is a rough compliance.
|
match (g.is_ascii(), is_current_window_ascii) {
|
||||||
*/
|
(true, true) => {
|
||||||
(false, false) if (((4 * (idx - current_window_start) / 3) + 3) & !3) > 33 => {
|
ret.push(g);
|
||||||
ret.push_str(&format!(
|
|
||||||
"=?UTF-8?B?{}?=",
|
|
||||||
BASE64_MIME
|
|
||||||
.encode(value[current_window_start..idx].as_bytes())
|
|
||||||
.trim()
|
|
||||||
));
|
|
||||||
if idx != value.len() - 1 {
|
|
||||||
ret.push(' ');
|
|
||||||
}
|
}
|
||||||
current_window_start = idx;
|
(true, false) => {
|
||||||
|
/* If !g.is_whitespace()
|
||||||
|
*
|
||||||
|
* Whitespaces inside encoded tokens must be greedily taken,
|
||||||
|
* instead of splitting each non-ascii word into separate encoded tokens. */
|
||||||
|
if !g.is_whitespace() {
|
||||||
|
ret.push_str(&format!(
|
||||||
|
"=?UTF-8?B?{}?=",
|
||||||
|
BASE64_MIME
|
||||||
|
.encode(value[current_window_start..idx].as_bytes())
|
||||||
|
.trim()
|
||||||
|
));
|
||||||
|
if idx != value.len() - 1 {
|
||||||
|
ret.push(' ');
|
||||||
|
}
|
||||||
|
is_current_window_ascii = true;
|
||||||
|
current_window_start = idx;
|
||||||
|
ret.push(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
current_window_start = idx;
|
||||||
|
is_current_window_ascii = false;
|
||||||
|
}
|
||||||
|
/* RFC2047 recommends:
|
||||||
|
* 'While there is no limit to the length of a multiple-line header field, each line of
|
||||||
|
* a header field that contains one or more 'encoded-word's is limited to 76
|
||||||
|
* characters.'
|
||||||
|
* This is a rough compliance.
|
||||||
|
*/
|
||||||
|
(false, false) if (((4 * (idx - current_window_start) / 3) + 3) & !3) > 33 => {
|
||||||
|
ret.push_str(&format!(
|
||||||
|
"=?UTF-8?B?{}?=",
|
||||||
|
BASE64_MIME
|
||||||
|
.encode(value[current_window_start..idx].as_bytes())
|
||||||
|
.trim()
|
||||||
|
));
|
||||||
|
if idx != value.len() - 1 {
|
||||||
|
ret.push(' ');
|
||||||
|
}
|
||||||
|
current_window_start = idx;
|
||||||
|
}
|
||||||
|
(false, false) => {}
|
||||||
}
|
}
|
||||||
(false, false) => {}
|
idx += std::mem::size_of::<char>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* If the last part of the header value is encoded, it won't be pushed inside the previous for
|
/* If the last part of the header value is encoded, it won't be pushed inside the previous for
|
||||||
|
|
|
@ -118,6 +118,7 @@ impl<T> From<std::sync::PoisonError<T>> for MeliError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "imap_backend")]
|
||||||
impl From<native_tls::HandshakeError<std::net::TcpStream>> for MeliError {
|
impl From<native_tls::HandshakeError<std::net::TcpStream>> for MeliError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(kind: native_tls::HandshakeError<std::net::TcpStream>) -> MeliError {
|
fn from(kind: native_tls::HandshakeError<std::net::TcpStream>) -> MeliError {
|
||||||
|
@ -125,6 +126,7 @@ impl From<native_tls::HandshakeError<std::net::TcpStream>> for MeliError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "imap_backend")]
|
||||||
impl From<native_tls::Error> for MeliError {
|
impl From<native_tls::Error> for MeliError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(kind: native_tls::Error) -> MeliError {
|
fn from(kind: native_tls::Error) -> MeliError {
|
||||||
|
|
|
@ -84,6 +84,7 @@ pub mod dbg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
extern crate text_processing;
|
extern crate text_processing;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -113,7 +114,6 @@ extern crate nom;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate data_encoding;
|
extern crate data_encoding;
|
||||||
extern crate encoding;
|
extern crate encoding;
|
||||||
extern crate memmap;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
use crate::email::parser::BytesExt;
|
use crate::email::parser::BytesExt;
|
||||||
use crate::email::*;
|
use crate::email::*;
|
||||||
use crate::structs::StackVec;
|
use crate::structs::StackVec;
|
||||||
|
|
||||||
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
use text_processing::grapheme_clusters::*;
|
use text_processing::grapheme_clusters::*;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -504,10 +506,19 @@ impl ThreadNode {
|
||||||
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
) {
|
) {
|
||||||
(Some(p), Some(c)) => envelopes[p]
|
(Some(p), Some(c)) => {
|
||||||
.subject()
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
.split_graphemes()
|
{
|
||||||
.cmp(&envelopes[c].subject().split_graphemes()),
|
envelopes[p]
|
||||||
|
.subject()
|
||||||
|
.split_graphemes()
|
||||||
|
.cmp(&envelopes[c].subject().split_graphemes())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
|
{
|
||||||
|
envelopes[p].subject().cmp(&envelopes[c].subject())
|
||||||
|
}
|
||||||
|
}
|
||||||
(Some(_), None) => Ordering::Greater,
|
(Some(_), None) => Ordering::Greater,
|
||||||
(None, Some(_)) => Ordering::Less,
|
(None, Some(_)) => Ordering::Less,
|
||||||
(None, None) => Ordering::Equal,
|
(None, None) => Ordering::Equal,
|
||||||
|
@ -523,10 +534,19 @@ impl ThreadNode {
|
||||||
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
buf.get(&probe).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
buf.get(&child).map(|n| n.message.as_ref()).unwrap_or(None),
|
||||||
) {
|
) {
|
||||||
(Some(p), Some(c)) => envelopes[c]
|
(Some(p), Some(c)) => {
|
||||||
.subject()
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
.split_graphemes()
|
{
|
||||||
.cmp(&envelopes[p].subject().split_graphemes()),
|
envelopes[c]
|
||||||
|
.subject()
|
||||||
|
.split_graphemes()
|
||||||
|
.cmp(&envelopes[p].subject().split_graphemes())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
|
{
|
||||||
|
envelopes[c].subject().cmp(&envelopes[p].subject())
|
||||||
|
}
|
||||||
|
}
|
||||||
(Some(_), None) => Ordering::Less,
|
(Some(_), None) => Ordering::Less,
|
||||||
(None, Some(_)) => Ordering::Greater,
|
(None, Some(_)) => Ordering::Greater,
|
||||||
(None, None) => Ordering::Equal,
|
(None, None) => Ordering::Equal,
|
||||||
|
@ -1078,9 +1098,16 @@ impl Threads {
|
||||||
}
|
}
|
||||||
let ma = &envelopes[&a.unwrap()];
|
let ma = &envelopes[&a.unwrap()];
|
||||||
let mb = &envelopes[&b.unwrap()];
|
let mb = &envelopes[&b.unwrap()];
|
||||||
ma.subject()
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
.split_graphemes()
|
{
|
||||||
.cmp(&mb.subject().split_graphemes())
|
ma.subject()
|
||||||
|
.split_graphemes()
|
||||||
|
.cmp(&mb.subject().split_graphemes())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
|
{
|
||||||
|
ma.subject().cmp(&mb.subject())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Asc) => {
|
(SortField::Subject, SortOrder::Asc) => {
|
||||||
let a = &self.thread_nodes[&a].message();
|
let a = &self.thread_nodes[&a].message();
|
||||||
|
@ -1100,10 +1127,18 @@ impl Threads {
|
||||||
}
|
}
|
||||||
let ma = &envelopes[&a.unwrap()];
|
let ma = &envelopes[&a.unwrap()];
|
||||||
let mb = &envelopes[&b.unwrap()];
|
let mb = &envelopes[&b.unwrap()];
|
||||||
mb.subject()
|
#[cfg(feature = "unicode_algorithms")]
|
||||||
.as_ref()
|
{
|
||||||
.split_graphemes()
|
mb.subject()
|
||||||
.cmp(&ma.subject().split_graphemes())
|
.as_ref()
|
||||||
|
.split_graphemes()
|
||||||
|
.cmp(&ma.subject().split_graphemes())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unicode_algorithms"))]
|
||||||
|
{
|
||||||
|
mb.subject().as_ref().cmp(&ma.subject())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue