melib: turn unicode algos and backends into features

embed
Manos Pitsidianakis 2019-09-21 21:23:06 +03:00
parent f066f35410
commit 6e75160b70
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
6 changed files with 191 additions and 69 deletions

View File

@ -12,16 +12,24 @@ crossbeam = "0.7.2"
data-encoding = "2.1.1"
encoding = "0.2.33"
fnv = "1.0.3"
memmap = "0.5.2"
memmap = { version = "0.5.2", optional = true }
nom = "3.2.0"
notify = "4.0.1"
notify-rust = "^3"
notify = { version = "4.0.1", optional = true }
notify-rust = { version = "^3", optional = true }
termion = "1.5.1"
xdg = "2.1.0"
native-tls = "0.2"
native-tls = { version ="0.2", optional=true }
serde = "1.0.71"
serde_derive = "1.0.71"
bincode = "1.0.1"
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",]}
[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"]

View File

@ -18,16 +18,22 @@
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
#[cfg(feature = "imap_backend")]
pub mod imap;
#[cfg(feature = "maildir_backend")]
pub mod maildir;
#[cfg(feature = "mbox_backend")]
pub mod mbox;
#[cfg(feature = "imap_backend")]
pub use self::imap::ImapType;
use crate::async_workers::*;
use crate::conf::AccountSettings;
use crate::error::{MeliError, Result};
//use mailbox::backends::imap::ImapType;
#[cfg(feature = "maildir_backend")]
use self::maildir::MaildirType;
#[cfg(feature = "mbox_backend")]
use self::mbox::MboxType;
use super::email::{Envelope, EnvelopeHash, Flag};
use std::fmt;
@ -57,18 +63,27 @@ impl Backends {
let mut b = Backends {
map: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
};
b.register(
"maildir".to_string(),
Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
);
b.register(
"mbox".to_string(),
Box::new(|| Box::new(|f, i| Box::new(MboxType::new(f, i)))),
);
b.register(
"imap".to_string(),
Box::new(|| Box::new(|f, i| Box::new(ImapType::new(f, i)))),
);
#[cfg(feature = "maildir_backend")]
{
b.register(
"maildir".to_string(),
Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
);
}
#[cfg(feature = "mbox_backend")]
{
b.register(
"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
}

View File

@ -1,22 +1,51 @@
use super::*;
#[cfg(feature = "unicode_algorithms")]
use text_processing::grapheme_clusters::Graphemes;
pub fn encode_header(value: &str) -> String {
let mut ret = String::with_capacity(value.len());
let graphemes = value.graphemes_indices();
let mut is_current_window_ascii = true;
let mut current_window_start = 0;
for (idx, g) in graphemes {
match (g.is_ascii(), is_current_window_ascii) {
(true, true) => {
ret.push_str(g);
}
(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.split_whitespace().collect::<Vec<&str>>().is_empty() {
#[cfg(feature = "unicode_algorithms")]
{
let graphemes = value.graphemes_indices();
for (idx, g) in graphemes {
match (g.is_ascii(), is_current_window_ascii) {
(true, true) => {
ret.push_str(g);
}
(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.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!(
"=?UTF-8?B?{}?=",
BASE64_MIME
@ -26,34 +55,67 @@ pub fn encode_header(value: &str) -> String {
if idx != value.len() - 1 {
ret.push(' ');
}
is_current_window_ascii = true;
current_window_start = idx;
ret.push_str(g);
}
(false, false) => {}
}
(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(' ');
}
}
#[cfg(not(feature = "unicode_algorithms"))]
{
/* TODO: test this. If it works as fine as the one above, there's no need to keep the above
* implementation.*/
let mut idx = 0;
for g in value.chars() {
match (g.is_ascii(), is_current_window_ascii) {
(true, true) => {
ret.push(g);
}
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

View File

@ -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 {
#[inline]
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 {
#[inline]
fn from(kind: native_tls::Error) -> MeliError {

View File

@ -84,6 +84,7 @@ pub mod dbg {
}
}
#[cfg(feature = "unicode_algorithms")]
extern crate text_processing;
#[macro_use]
@ -113,7 +114,6 @@ extern crate nom;
extern crate chrono;
extern crate data_encoding;
extern crate encoding;
extern crate memmap;
#[macro_use]
extern crate bitflags;

View File

@ -35,6 +35,8 @@
use crate::email::parser::BytesExt;
use crate::email::*;
use crate::structs::StackVec;
#[cfg(feature = "unicode_algorithms")]
use text_processing::grapheme_clusters::*;
use uuid::Uuid;
@ -504,10 +506,19 @@ impl ThreadNode {
buf.get(&probe).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]
.subject()
.split_graphemes()
.cmp(&envelopes[c].subject().split_graphemes()),
(Some(p), Some(c)) => {
#[cfg(feature = "unicode_algorithms")]
{
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,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
@ -523,10 +534,19 @@ impl ThreadNode {
buf.get(&probe).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]
.subject()
.split_graphemes()
.cmp(&envelopes[p].subject().split_graphemes()),
(Some(p), Some(c)) => {
#[cfg(feature = "unicode_algorithms")]
{
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,
(None, Some(_)) => Ordering::Greater,
(None, None) => Ordering::Equal,
@ -1078,9 +1098,16 @@ impl Threads {
}
let ma = &envelopes[&a.unwrap()];
let mb = &envelopes[&b.unwrap()];
ma.subject()
.split_graphemes()
.cmp(&mb.subject().split_graphemes())
#[cfg(feature = "unicode_algorithms")]
{
ma.subject()
.split_graphemes()
.cmp(&mb.subject().split_graphemes())
}
#[cfg(not(feature = "unicode_algorithms"))]
{
ma.subject().cmp(&mb.subject())
}
}
(SortField::Subject, SortOrder::Asc) => {
let a = &self.thread_nodes[&a].message();
@ -1100,10 +1127,18 @@ impl Threads {
}
let ma = &envelopes[&a.unwrap()];
let mb = &envelopes[&b.unwrap()];
mb.subject()
.as_ref()
.split_graphemes()
.cmp(&ma.subject().split_graphemes())
#[cfg(feature = "unicode_algorithms")]
{
mb.subject()
.as_ref()
.split_graphemes()
.cmp(&ma.subject().split_graphemes())
}
#[cfg(not(feature = "unicode_algorithms"))]
{
mb.subject().as_ref().cmp(&ma.subject())
}
}
});
}