parent
7a6fc1ce94
commit
b98a04f35b
|
@ -9,35 +9,35 @@ use self::test::Bencher;
|
|||
#[bench]
|
||||
fn bench_threads_1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let folder = Folder::new(String::from(""), vec![]);
|
||||
let folder = Folder::new(String::from(""), String::from(""), vec![]);
|
||||
MaildirType::new("").multicore(1, &folder)
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let folder = Folder::new(String::from(""), vec![]);
|
||||
let folder = Folder::new(String::from(""), String::from(""), vec![]);
|
||||
MaildirType::new("").multicore(2, &folder)
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_3(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let folder = Folder::new(String::from(""), vec![]);
|
||||
let folder = Folder::new(String::from(""), String::from(""), vec![]);
|
||||
MaildirType::new("").multicore(3, &folder)
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_4(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let folder = Folder::new(String::from(""), vec![]);
|
||||
let folder = Folder::new(String::from(""), String::from(""), vec![]);
|
||||
MaildirType::new("").multicore(4, &folder)
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn bench_threads_6(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let folder = Folder::new(String::from(""), vec![]);
|
||||
let folder = Folder::new(String::from(""), String::from(""), vec![]);
|
||||
MaildirType::new("").multicore(6, &folder)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -26,53 +26,22 @@ pub mod pager;
|
|||
|
||||
use pager::PagerSettings;
|
||||
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::hash::Hasher;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Folder {
|
||||
hash: u64,
|
||||
name: String,
|
||||
path: String,
|
||||
children: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
pub fn new(path: String, file_name: String, children: Vec<usize>) -> Self {
|
||||
let mut h = DefaultHasher::new();
|
||||
h.write(&path.as_bytes());
|
||||
Folder {
|
||||
hash: h.finish(),
|
||||
name: file_name,
|
||||
path: path,
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
pub fn hash(&self) -> u64 {
|
||||
self.hash
|
||||
}
|
||||
pub fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn children(&self) -> &Vec<usize> {
|
||||
&self.children
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct FileAccount {
|
||||
folders: String,
|
||||
pub struct FileAccount {
|
||||
root_folder: String,
|
||||
format: String,
|
||||
sent_folder: String,
|
||||
threaded: bool,
|
||||
}
|
||||
|
||||
impl FileAccount {
|
||||
pub fn folder(&self) -> &str {
|
||||
&self.root_folder
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct FileSettings {
|
||||
accounts: HashMap<String, FileAccount>,
|
||||
|
@ -82,7 +51,7 @@ struct FileSettings {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct AccountSettings {
|
||||
name: String,
|
||||
pub folders: Vec<Folder>,
|
||||
root_folder: String,
|
||||
format: String,
|
||||
pub sent_folder: String,
|
||||
pub threaded: bool,
|
||||
|
@ -95,6 +64,9 @@ impl AccountSettings {
|
|||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn root_folder(&self) -> &str {
|
||||
&self.root_folder
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
@ -126,53 +98,20 @@ impl Settings {
|
|||
let mut s: HashMap<String, AccountSettings> = HashMap::new();
|
||||
|
||||
for (id, x) in fs.accounts {
|
||||
let mut folders = Vec::new();
|
||||
fn recurse_folders<P: AsRef<Path>>(folders: &mut Vec<Folder>, p: P) -> Vec<usize> {
|
||||
let mut children = Vec::new();
|
||||
for mut f in fs::read_dir(p).unwrap() {
|
||||
for f in f.iter_mut() {
|
||||
{
|
||||
let path = f.path();
|
||||
if path.ends_with("cur")
|
||||
|| path.ends_with("new")
|
||||
|| path.ends_with("tmp")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if path.is_dir() {
|
||||
let path_children = recurse_folders(folders, &path);
|
||||
folders.push(Folder::new(
|
||||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
path_children,
|
||||
));
|
||||
children.push(folders.len() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children
|
||||
let format = x.format.to_lowercase();
|
||||
let sent_folder = x.sent_folder;
|
||||
let threaded = x.threaded;
|
||||
let root_folder = x.root_folder;
|
||||
|
||||
let acc = AccountSettings {
|
||||
name: id.clone(),
|
||||
root_folder,
|
||||
format,
|
||||
sent_folder,
|
||||
threaded,
|
||||
};
|
||||
let path = PathBuf::from(&x.folders);
|
||||
let path_children = recurse_folders(&mut folders, &path);
|
||||
if path.is_dir() {
|
||||
folders.push(Folder::new(
|
||||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
path_children,
|
||||
));
|
||||
}
|
||||
//folders.sort_by(|a, b| b.name.cmp(&a.name));
|
||||
s.insert(
|
||||
id.clone(),
|
||||
AccountSettings {
|
||||
name: id.clone(),
|
||||
folders: folders,
|
||||
format: x.format.to_lowercase(),
|
||||
sent_folder: x.sent_folder.clone(),
|
||||
threaded: x.threaded,
|
||||
},
|
||||
);
|
||||
|
||||
s.insert(id, acc);
|
||||
}
|
||||
|
||||
Settings {
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
* An error object for `melib`
|
||||
*/
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::result;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use nom;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
|
||||
use async::*;
|
||||
use conf::{AccountSettings, Folder};
|
||||
use conf::AccountSettings;
|
||||
use mailbox::backends::{Backends, RefreshEventConsumer};
|
||||
use mailbox::*;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
@ -47,15 +47,15 @@ pub struct Account {
|
|||
}
|
||||
|
||||
impl Account {
|
||||
pub fn new(name: String, settings: AccountSettings, backends: &Backends) -> Self {
|
||||
let sent_folder = settings
|
||||
.folders
|
||||
pub fn new(name: String, settings: AccountSettings, map: &Backends) -> Self {
|
||||
let backend = map.get(settings.format())(&settings);
|
||||
let ref_folders: Vec<Folder> = backend.folders();
|
||||
let mut folders: Vec<Option<Result<Mailbox>>> = Vec::with_capacity(ref_folders.len());
|
||||
let mut workers: Vec<Worker> = Vec::new();
|
||||
let sent_folder = ref_folders
|
||||
.iter()
|
||||
.position(|x| *x.path() == settings.sent_folder);
|
||||
let mut folders = Vec::with_capacity(settings.folders.len());
|
||||
let mut workers = Vec::new();
|
||||
let backend = backends.get(settings.format());
|
||||
for f in &settings.folders {
|
||||
.position(|x: &Folder| x.name() == settings.sent_folder);
|
||||
for f in ref_folders {
|
||||
folders.push(None);
|
||||
let mut handle = backend.get(&f);
|
||||
workers.push(Some(handle));
|
||||
|
@ -73,14 +73,14 @@ impl Account {
|
|||
}
|
||||
}
|
||||
pub fn watch(&self, r: RefreshEventConsumer) -> () {
|
||||
self.backend.watch(r, &self.settings.folders[..]);
|
||||
self.backend.watch(r).unwrap();
|
||||
}
|
||||
/* This doesn't represent the number of correctly parsed mailboxes though */
|
||||
pub fn len(&self) -> usize {
|
||||
self.folders.len()
|
||||
}
|
||||
pub fn list_folders(&self) -> Vec<Folder> {
|
||||
self.settings.folders.clone()
|
||||
self.backend.folders()
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
|
@ -89,7 +89,8 @@ impl Account {
|
|||
&mut self.workers
|
||||
}
|
||||
fn load_mailbox(&mut self, index: usize, envelopes: Result<Vec<Envelope>>) -> () {
|
||||
let folder = &self.settings.folders[index];
|
||||
let folders = self.backend.folders();
|
||||
let folder = &folders[index];
|
||||
if self.sent_folder.is_some() {
|
||||
let id = self.sent_folder.unwrap();
|
||||
if id == index {
|
||||
|
@ -105,7 +106,7 @@ impl Account {
|
|||
)
|
||||
}
|
||||
};
|
||||
let sent_path = &self.settings.folders[id];
|
||||
let sent_path = &folders[id];
|
||||
if sent[0].is_none() {
|
||||
sent[0] = Some(Mailbox::new(sent_path, &None, envelopes.clone()));
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
use async::*;
|
||||
use conf::Folder;
|
||||
use error::Result;
|
||||
use mailbox::backends::{MailBackend, RefreshEventConsumer};
|
||||
use mailbox::backends::{MailBackend, RefreshEventConsumer, Folder};
|
||||
use mailbox::email::Envelope;
|
||||
|
||||
/// `BackendOp` implementor for Imap
|
||||
|
@ -35,7 +35,6 @@ impl ImapOp {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
impl BackendOp for ImapOp {
|
||||
fn description(&self) -> String {
|
||||
|
@ -53,7 +52,7 @@ impl BackendOp for ImapOp {
|
|||
fn fetch_flags(&self) -> Flag {
|
||||
unimplemented!();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Imap backend
|
||||
#[derive(Debug)]
|
||||
|
@ -72,4 +71,4 @@ impl ImapType {
|
|||
pub fn new(_path: &str) -> Self {
|
||||
ImapType {}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -20,10 +20,11 @@
|
|||
*/
|
||||
|
||||
use async::*;
|
||||
use conf::Folder;
|
||||
use conf::AccountSettings;
|
||||
use error::{MeliError, Result};
|
||||
use mailbox::backends::{
|
||||
BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer,
|
||||
BackendFolder, BackendOp, BackendOpGenerator, Folder, MailBackend, RefreshEvent,
|
||||
RefreshEventConsumer,
|
||||
};
|
||||
use mailbox::email::parser;
|
||||
use mailbox::email::{Envelope, Flag};
|
||||
|
@ -43,7 +44,7 @@ use memmap::{Mmap, Protection};
|
|||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs;
|
||||
use std::hash::Hasher;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// `BackendOp` implementor for Maildir
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -152,38 +153,42 @@ impl BackendOp for MaildirOp {
|
|||
/// Maildir backend https://cr.yp.to/proto/maildir.html
|
||||
#[derive(Debug)]
|
||||
pub struct MaildirType {
|
||||
folders: Vec<MaildirFolder>,
|
||||
path: String,
|
||||
idx: (usize, usize),
|
||||
}
|
||||
|
||||
impl MailBackend for MaildirType {
|
||||
fn folders(&self) -> Vec<Folder> {
|
||||
self.folders.iter().map(|f| f.clone()).collect()
|
||||
}
|
||||
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
self.multicore(4, folder)
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
|
||||
let folders = folders.to_vec();
|
||||
|
||||
fn watch(&self, sender: RefreshEventConsumer) -> Result<()> {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
for f in &self.folders {
|
||||
if f.is_valid().is_err() {
|
||||
continue;
|
||||
}
|
||||
eprintln!("watching {:?}", f);
|
||||
let mut p = PathBuf::from(&f.path);
|
||||
p.push("cur");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
p.pop();
|
||||
p.push("new");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
}
|
||||
thread::Builder::new()
|
||||
.name("folder watch".to_string())
|
||||
.spawn(move || {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
for f in folders {
|
||||
if MaildirType::is_valid(&f).is_err() {
|
||||
continue;
|
||||
}
|
||||
eprintln!("watching {}", f.path());
|
||||
let mut p = PathBuf::from(&f.path());
|
||||
p.push("cur");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
p.pop();
|
||||
p.push("new");
|
||||
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
|
||||
}
|
||||
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
||||
let _watcher = watcher;
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(event) => match event {
|
||||
DebouncedEvent::Create(mut pathbuf) | DebouncedEvent::Remove(mut pathbuf) => {
|
||||
DebouncedEvent::Create(mut pathbuf)
|
||||
| DebouncedEvent::Remove(mut pathbuf) => {
|
||||
let path = if pathbuf.is_dir() {
|
||||
if pathbuf.ends_with("cur") | pathbuf.ends_with("new") {
|
||||
pathbuf.pop();
|
||||
|
@ -207,45 +212,76 @@ impl MailBackend for MaildirType {
|
|||
Err(e) => eprintln!("watch error: {:?}", e),
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MaildirType {
|
||||
pub fn new(path: &str, idx: (usize, usize)) -> Self {
|
||||
MaildirType {
|
||||
path: path.to_string(),
|
||||
idx: idx,
|
||||
}
|
||||
}
|
||||
fn is_valid(f: &Folder) -> Result<()> {
|
||||
let path = f.path();
|
||||
let mut p = PathBuf::from(path);
|
||||
for d in &["cur", "new", "tmp"] {
|
||||
p.push(d);
|
||||
if !p.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"{} is not a valid maildir folder",
|
||||
path
|
||||
)));
|
||||
pub fn new(f: &AccountSettings) -> Self {
|
||||
let mut folders: Vec<MaildirFolder> = Vec::new();
|
||||
fn recurse_folders<P: AsRef<Path>>(folders: &mut Vec<MaildirFolder>, p: P) -> Vec<usize> {
|
||||
let mut children = Vec::new();
|
||||
for mut f in fs::read_dir(p).unwrap() {
|
||||
for f in f.iter_mut() {
|
||||
{
|
||||
let path = f.path();
|
||||
if path.ends_with("cur") || path.ends_with("new") || path.ends_with("tmp") {
|
||||
continue;
|
||||
}
|
||||
if path.is_dir() {
|
||||
let path_children = recurse_folders(folders, &path);
|
||||
if let Ok(f) = MaildirFolder::new(
|
||||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
path_children,
|
||||
) {
|
||||
folders.push(f);
|
||||
children.push(folders.len() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
children
|
||||
};
|
||||
let path = PathBuf::from(f.root_folder());
|
||||
let path_children = recurse_folders(&mut folders, &path);
|
||||
if path.is_dir() {
|
||||
if let Ok(f) = MaildirFolder::new(
|
||||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
path_children,
|
||||
) {
|
||||
folders.push(f);
|
||||
}
|
||||
p.pop();
|
||||
}
|
||||
Ok(())
|
||||
MaildirType {
|
||||
folders,
|
||||
path: f.root_folder().to_string(),
|
||||
}
|
||||
}
|
||||
fn owned_folder_idx(&self, folder: &Folder) -> usize {
|
||||
for (idx, f) in self.folders.iter().enumerate() {
|
||||
if f.hash() == folder.hash() {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn multicore(&self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
// TODO: Avoid clone
|
||||
let folder = folder.clone();
|
||||
let folder: &MaildirFolder = &self.folders[self.owned_folder_idx(folder)];
|
||||
let path = folder.path().to_string();
|
||||
let name = format!("parsing {:?}", folder.name());
|
||||
|
||||
thread::Builder::new()
|
||||
.name(format!("parsing {:?}", folder))
|
||||
.name(name)
|
||||
.spawn(move || {
|
||||
MaildirType::is_valid(&folder)?;
|
||||
let path = folder.path();
|
||||
let mut path = PathBuf::from(path);
|
||||
path.push("cur");
|
||||
let iter = path.read_dir()?;
|
||||
|
@ -310,3 +346,64 @@ impl MaildirType {
|
|||
w.build(handle)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MaildirFolder {
|
||||
hash: u64,
|
||||
name: String,
|
||||
path: String,
|
||||
children: Vec<usize>,
|
||||
}
|
||||
|
||||
impl MaildirFolder {
|
||||
pub fn new(path: String, file_name: String, children: Vec<usize>) -> Result<Self> {
|
||||
let mut h = DefaultHasher::new();
|
||||
h.write(&path.as_bytes());
|
||||
|
||||
let ret = MaildirFolder {
|
||||
hash: h.finish(),
|
||||
name: file_name,
|
||||
path: path,
|
||||
children: children,
|
||||
};
|
||||
ret.is_valid()?;
|
||||
Ok(ret)
|
||||
}
|
||||
pub fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
fn is_valid(&self) -> Result<()> {
|
||||
let path = self.path();
|
||||
let mut p = PathBuf::from(path);
|
||||
for d in &["cur", "new", "tmp"] {
|
||||
p.push(d);
|
||||
if !p.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"{} is not a valid maildir folder",
|
||||
path
|
||||
)));
|
||||
}
|
||||
p.pop();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl BackendFolder for MaildirFolder {
|
||||
fn hash(&self) -> u64 {
|
||||
self.hash
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn children(&self) -> &Vec<usize> {
|
||||
&self.children
|
||||
}
|
||||
fn clone(&self) -> Folder {
|
||||
Box::new(MaildirFolder {
|
||||
hash: self.hash,
|
||||
name: self.name.clone(),
|
||||
path: self.path.clone(),
|
||||
children: self.children.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,10 @@
|
|||
* https://wiki2.dovecot.org/MailboxFormat/mbox
|
||||
*/
|
||||
|
||||
/*
|
||||
use async::*;
|
||||
use conf::Folder;
|
||||
use error::Result;
|
||||
use mailbox::backends::{MailBackend, RefreshEventConsumer};
|
||||
use mailbox::backends::{MailBackend, RefreshEventConsumer, Folder};
|
||||
use mailbox::email::Envelope;
|
||||
|
||||
/// `BackendOp` implementor for Mbox
|
||||
|
@ -39,7 +39,6 @@ impl MboxOp {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl BackendOp for MboxOp {
|
||||
fn description(&self) -> String {
|
||||
unimplemented!();
|
||||
|
@ -60,7 +59,6 @@ impl BackendOp for MboxOp {
|
|||
unimplemented!()
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// Mbox backend
|
||||
#[derive(Debug)]
|
||||
|
@ -80,3 +78,4 @@ impl MboxType {
|
|||
MboxType {}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -23,22 +23,25 @@ pub mod maildir;
|
|||
pub mod mbox;
|
||||
|
||||
use async::*;
|
||||
use conf::Folder;
|
||||
use conf::AccountSettings;
|
||||
use error::Result;
|
||||
use mailbox::backends::imap::ImapType;
|
||||
//use mailbox::backends::imap::ImapType;
|
||||
//use mailbox::backends::mbox::MboxType;
|
||||
use mailbox::backends::maildir::MaildirType;
|
||||
use mailbox::backends::mbox::MboxType;
|
||||
use mailbox::email::{Envelope, Flag};
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
|
||||
extern crate fnv;
|
||||
use self::fnv::FnvHashMap;
|
||||
use std;
|
||||
|
||||
pub type BackendCreator = Box<Fn(&AccountSettings) -> Box<MailBackend>>;
|
||||
|
||||
/// A hashmap containing all available mail backends.
|
||||
/// An abstraction over any available backends.
|
||||
pub struct Backends {
|
||||
map: FnvHashMap<std::string::String, Box<Fn() -> Box<MailBackend>>>,
|
||||
map: FnvHashMap<std::string::String, Box<Fn() -> BackendCreator>>,
|
||||
}
|
||||
|
||||
impl Backends {
|
||||
|
@ -48,20 +51,20 @@ impl Backends {
|
|||
};
|
||||
b.register(
|
||||
"maildir".to_string(),
|
||||
Box::new(|| Box::new(MaildirType::new("", (0, 0)))),
|
||||
Box::new(|| Box::new(|f| Box::new(MaildirType::new(f)))),
|
||||
);
|
||||
b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new(""))));
|
||||
b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new(""))));
|
||||
//b.register("mbox".to_string(), Box::new(|| Box::new(MboxType::new(""))));
|
||||
//b.register("imap".to_string(), Box::new(|| Box::new(ImapType::new(""))));
|
||||
b
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Box<MailBackend> {
|
||||
pub fn get(&self, key: &str) -> BackendCreator {
|
||||
if !self.map.contains_key(key) {
|
||||
panic!("{} is not a valid mail backend", key);
|
||||
}
|
||||
self.map[key]()
|
||||
}
|
||||
pub fn register(&mut self, key: String, backend: Box<Fn() -> Box<MailBackend>>) -> () {
|
||||
pub fn register(&mut self, key: String, backend: Box<Fn() -> BackendCreator>) -> () {
|
||||
if self.map.contains_key(&key) {
|
||||
panic!("{} is an already registered backend", key);
|
||||
}
|
||||
|
@ -90,8 +93,8 @@ impl RefreshEventConsumer {
|
|||
}
|
||||
pub trait MailBackend: ::std::fmt::Debug {
|
||||
fn get(&self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
||||
fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> ();
|
||||
//fn new(folders: &Vec<String>) -> Box<Self>;
|
||||
fn watch(&self, sender: RefreshEventConsumer) -> Result<()>;
|
||||
fn folders(&self) -> Vec<Folder>;
|
||||
//login function
|
||||
}
|
||||
|
||||
|
@ -168,3 +171,37 @@ impl fmt::Debug for BackendOpGenerator {
|
|||
write!(f, "BackendOpGenerator: {}", op.description())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BackendFolder: Debug {
|
||||
fn hash(&self) -> u64;
|
||||
fn name(&self) -> &str;
|
||||
fn clone(&self) -> Folder;
|
||||
fn children(&self) -> &Vec<usize>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DummyFolder {
|
||||
v: Vec<usize>,
|
||||
}
|
||||
|
||||
impl BackendFolder for DummyFolder {
|
||||
fn hash(&self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
""
|
||||
}
|
||||
fn clone(&self) -> Folder {
|
||||
folder_default()
|
||||
}
|
||||
fn children(&self) -> &Vec<usize> {
|
||||
&self.v
|
||||
}
|
||||
}
|
||||
pub fn folder_default() -> Folder {
|
||||
Box::new(DummyFolder {
|
||||
v: Vec::with_capacity(0),
|
||||
})
|
||||
}
|
||||
|
||||
pub type Folder = Box<BackendFolder>;
|
||||
|
|
|
@ -20,7 +20,7 @@ impl Default for Charset {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a[u8]> for Charset {
|
||||
impl<'a> From<&'a [u8]> for Charset {
|
||||
fn from(b: &'a [u8]) -> Self {
|
||||
// TODO: Case insensitivity
|
||||
match b {
|
||||
|
@ -69,9 +69,10 @@ pub enum ContentType {
|
|||
|
||||
impl Default for ContentType {
|
||||
fn default() -> Self {
|
||||
ContentType::Text{ charset: Charset::UTF8 }
|
||||
ContentType::Text {
|
||||
charset: Charset::UTF8,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for ContentType {
|
||||
|
@ -128,4 +129,3 @@ pub enum ContentTransferEncoding {
|
|||
QuotedPrintable,
|
||||
Other { tag: Vec<u8> },
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
use data_encoding::BASE64_MIME;
|
||||
use mailbox::email::parser;
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
pub use mailbox::email::attachment_types::*;
|
||||
|
||||
|
@ -44,7 +44,14 @@ impl fmt::Debug for AttachmentType {
|
|||
match self {
|
||||
AttachmentType::Data { .. } => write!(f, "AttachmentType::Data {{ .. }}"),
|
||||
AttachmentType::Text { .. } => write!(f, "AttachmentType::Text {{ .. }}"),
|
||||
AttachmentType::Multipart { of_type, subattachments } => write!(f, "AttachmentType::Multipart {{ of_type: {:?},\nsubattachments: {:?} }}", of_type, subattachments),
|
||||
AttachmentType::Multipart {
|
||||
of_type,
|
||||
subattachments,
|
||||
} => write!(
|
||||
f,
|
||||
"AttachmentType::Multipart {{ of_type: {:?},\nsubattachments: {:?} }}",
|
||||
of_type, subattachments
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +103,7 @@ impl fmt::Display for AttachmentType {
|
|||
impl AttachmentBuilder {
|
||||
pub fn new(content: &[u8]) -> Self {
|
||||
AttachmentBuilder {
|
||||
content_type: (Default::default() , ContentSubType::Plain),
|
||||
content_type: (Default::default(), ContentSubType::Plain),
|
||||
content_transfer_encoding: ContentTransferEncoding::_7Bit,
|
||||
raw: content.to_vec(),
|
||||
}
|
||||
|
@ -124,7 +131,9 @@ impl AttachmentBuilder {
|
|||
self.content_type.0 = Default::default();
|
||||
for (n, v) in params {
|
||||
if n.eq_ignore_ascii_case(b"charset") {
|
||||
self.content_type.0 = ContentType::Text { charset: Charset::from(v) };
|
||||
self.content_type.0 = ContentType::Text {
|
||||
charset: Charset::from(v),
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +177,7 @@ impl AttachmentBuilder {
|
|||
fn decode(&self) -> Vec<u8> {
|
||||
// TODO merge this and standalone decode() function
|
||||
let charset = match self.content_type.0 {
|
||||
ContentType::Text{ charset: c } => c,
|
||||
ContentType::Text { charset: c } => c,
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
|
@ -180,13 +189,17 @@ impl AttachmentBuilder {
|
|||
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_bytes(&self.raw)
|
||||
.to_full_result()
|
||||
.unwrap(),
|
||||
ContentTransferEncoding::_7Bit
|
||||
| ContentTransferEncoding::_8Bit
|
||||
| ContentTransferEncoding::Other { .. } => self.raw.to_vec(),
|
||||
ContentTransferEncoding::_7Bit
|
||||
| ContentTransferEncoding::_8Bit
|
||||
| ContentTransferEncoding::Other { .. } => self.raw.to_vec(),
|
||||
};
|
||||
|
||||
let decoded_result = parser::decode_charset(&bytes, charset);
|
||||
decoded_result.as_ref().map(|v| v.as_bytes()).unwrap_or_else(|_| &self.raw).to_vec()
|
||||
decoded_result
|
||||
.as_ref()
|
||||
.map(|v| v.as_bytes())
|
||||
.unwrap_or_else(|_| &self.raw)
|
||||
.to_vec()
|
||||
}
|
||||
pub fn build(self) -> Attachment {
|
||||
let attachment_type = match self.content_type.0 {
|
||||
|
@ -197,7 +210,9 @@ impl AttachmentBuilder {
|
|||
let multipart_type = match self.content_type.1 {
|
||||
ContentSubType::Other { ref tag } => match &tag[..] {
|
||||
b"mixed" | b"Mixed" | b"MIXED" => MultipartType::Mixed,
|
||||
b"alternative" | b"Alternative" | b"ALTERNATIVE" => MultipartType::Alternative,
|
||||
b"alternative" | b"Alternative" | b"ALTERNATIVE" => {
|
||||
MultipartType::Alternative
|
||||
}
|
||||
b"digest" | b"Digest" | b"DIGEST" => MultipartType::Digest,
|
||||
_ => MultipartType::Unsupported { tag: tag.clone() },
|
||||
},
|
||||
|
@ -261,14 +276,15 @@ impl AttachmentBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Attachment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.attachment_type {
|
||||
AttachmentType::Data { .. } => {
|
||||
write!(f, "Data attachment of type {}", self.mime_type())
|
||||
}
|
||||
AttachmentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
|
||||
AttachmentType::Text { .. } => {
|
||||
write!(f, "Text attachment of type {}", self.mime_type())
|
||||
}
|
||||
AttachmentType::Multipart {
|
||||
of_type: ref multipart_type,
|
||||
subattachments: ref sub_att_vec,
|
||||
|
@ -276,10 +292,16 @@ impl fmt::Display for Attachment {
|
|||
write!(
|
||||
f,
|
||||
"{} attachment with {} subs",
|
||||
self.mime_type(), sub_att_vec.len()
|
||||
self.mime_type(),
|
||||
sub_att_vec.len()
|
||||
)
|
||||
} else {
|
||||
write!(f, "{} attachment with {} subs", self.mime_type(), sub_att_vec.len())
|
||||
write!(
|
||||
f,
|
||||
"{} attachment with {} subs",
|
||||
self.mime_type(),
|
||||
sub_att_vec.len()
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +362,7 @@ impl Attachment {
|
|||
for a in sub_att_vec {
|
||||
count_recursive(a, ret);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +396,7 @@ fn decode_rec_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment) -> Vec<
|
|||
return filter(a);
|
||||
}
|
||||
match a.attachment_type {
|
||||
AttachmentType::Data { .. } => { Vec::new()},
|
||||
AttachmentType::Data { .. } => Vec::new(),
|
||||
AttachmentType::Text { .. } => decode_helper(a, filter),
|
||||
AttachmentType::Multipart {
|
||||
of_type: ref multipart_type,
|
||||
|
@ -405,7 +427,7 @@ fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment) -> Vec<u8>>
|
|||
}
|
||||
|
||||
let charset = match a.content_type.0 {
|
||||
ContentType::Text{ charset: c } => c,
|
||||
ContentType::Text { charset: c } => c,
|
||||
_ => Default::default(),
|
||||
};
|
||||
|
||||
|
@ -424,7 +446,11 @@ fn decode_helper(a: &Attachment, filter: &Option<Box<Fn(&Attachment) -> Vec<u8>>
|
|||
|
||||
if a.content_type().0.is_text() {
|
||||
let decoded_result = parser::decode_charset(&bytes, charset);
|
||||
decoded_result.as_ref().map(|v| v.as_bytes()).unwrap_or_else(|_| a.bytes()).to_vec()
|
||||
decoded_result
|
||||
.as_ref()
|
||||
.map(|v| v.as_bytes())
|
||||
.unwrap_or_else(|_| a.bytes())
|
||||
.to_vec()
|
||||
} else {
|
||||
bytes.to_vec()
|
||||
}
|
||||
|
|
|
@ -203,6 +203,33 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EnvelopeBuilder {
|
||||
from: Option<Vec<Address>>,
|
||||
to: Vec<Address>,
|
||||
body: Option<Attachment>,
|
||||
in_reply_to: Option<MessageID>,
|
||||
flags: Flag,
|
||||
}
|
||||
|
||||
impl EnvelopeBuilder {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn build(self) -> Envelope {
|
||||
unimplemented!();
|
||||
|
||||
/*
|
||||
* 1. Check for date. Default is now
|
||||
* 2.
|
||||
Envelope {
|
||||
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/// `Envelope` represents all the data of an email we need to know.
|
||||
///
|
||||
/// Attachments (the email's body) is parsed on demand with `body`.
|
||||
|
|
|
@ -166,7 +166,7 @@ named!(pub attachment<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>,
|
|||
|
||||
/* TODO: make a map of encodings and decoding functions so that they can be reused and easily
|
||||
* extended */
|
||||
use encoding::all::{ISO_8859_1, ISO_8859_2, ISO_8859_7, WINDOWS_1253, WINDOWS_1252, GBK};
|
||||
use encoding::all::{ISO_8859_1, ISO_8859_2, ISO_8859_7, WINDOWS_1252, WINDOWS_1253, GBK};
|
||||
|
||||
fn encoded_word(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
|
||||
if input.len() < 5 {
|
||||
|
@ -266,33 +266,15 @@ fn encoded_word(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
|
|||
|
||||
pub fn decode_charset(s: &[u8], charset: Charset) -> Result<String> {
|
||||
match charset {
|
||||
Charset::UTF8 | Charset::Ascii => {
|
||||
Ok(String::from_utf8(s.to_vec()).unwrap())
|
||||
}
|
||||
Charset::ISO8859_7 => {
|
||||
Ok(ISO_8859_7.decode(s, DecoderTrap::Strict)?)
|
||||
}
|
||||
Charset::ISO8859_1 => {
|
||||
Ok(ISO_8859_1.decode(s, DecoderTrap::Strict)?)
|
||||
}
|
||||
Charset::ISO8859_2 => {
|
||||
Ok(ISO_8859_2.decode(s, DecoderTrap::Strict)?)
|
||||
}
|
||||
Charset::GBK => {
|
||||
Ok(GBK.decode(s, DecoderTrap::Strict)?)
|
||||
}
|
||||
Charset::Windows1252 => {
|
||||
Ok(WINDOWS_1252.decode(s, DecoderTrap::Strict)?)
|
||||
},
|
||||
Charset::Windows1253 => {
|
||||
Ok(WINDOWS_1253.decode(s, DecoderTrap::Strict)?)
|
||||
},
|
||||
Charset::GB2312 => {
|
||||
unimplemented!()
|
||||
},
|
||||
Charset::UTF16 => {
|
||||
unimplemented!()
|
||||
},
|
||||
Charset::UTF8 | Charset::Ascii => Ok(String::from_utf8(s.to_vec()).unwrap()),
|
||||
Charset::ISO8859_7 => Ok(ISO_8859_7.decode(s, DecoderTrap::Strict)?),
|
||||
Charset::ISO8859_1 => Ok(ISO_8859_1.decode(s, DecoderTrap::Strict)?),
|
||||
Charset::ISO8859_2 => Ok(ISO_8859_2.decode(s, DecoderTrap::Strict)?),
|
||||
Charset::GBK => Ok(GBK.decode(s, DecoderTrap::Strict)?),
|
||||
Charset::Windows1252 => Ok(WINDOWS_1252.decode(s, DecoderTrap::Strict)?),
|
||||
Charset::Windows1253 => Ok(WINDOWS_1253.decode(s, DecoderTrap::Strict)?),
|
||||
Charset::GB2312 => unimplemented!(),
|
||||
Charset::UTF16 => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,12 +304,10 @@ named!(
|
|||
pub quoted_printable_bytes<Vec<u8>>,
|
||||
many0!(alt_complete!(
|
||||
preceded!(quoted_printable_soft_break, quoted_printable_byte) |
|
||||
preceded!(quoted_printable_soft_break, le_u8)
|
||||
| quoted_printable_byte | le_u8
|
||||
preceded!(quoted_printable_soft_break, le_u8) | quoted_printable_byte | le_u8
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
named!(
|
||||
encoded_word_list<Vec<u8>>,
|
||||
ws!(do_parse!(
|
||||
|
@ -397,7 +377,10 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> {
|
|||
IResult::Done(rest, raw) => {
|
||||
display_name.length = raw.find(b"<").unwrap().saturating_sub(1);
|
||||
address_spec.offset = display_name.length + 2;
|
||||
address_spec.length = raw.len().saturating_sub(display_name.length).saturating_sub(3);
|
||||
address_spec.length = raw
|
||||
.len()
|
||||
.saturating_sub(display_name.length)
|
||||
.saturating_sub(3);
|
||||
IResult::Done(
|
||||
rest,
|
||||
Address::Mailbox(MailboxAddress {
|
||||
|
|
|
@ -30,18 +30,16 @@ pub use self::email::*;
|
|||
/* Mail backends. Currently only maildir is supported */
|
||||
pub mod backends;
|
||||
use error::Result;
|
||||
use mailbox::backends::MailBackend;
|
||||
use mailbox::backends::{folder_default, Folder, MailBackend};
|
||||
pub mod accounts;
|
||||
pub use mailbox::accounts::Account;
|
||||
pub mod thread;
|
||||
pub use mailbox::thread::{build_threads, Container};
|
||||
|
||||
use conf::Folder;
|
||||
|
||||
use std::option::Option;
|
||||
|
||||
/// `Mailbox` represents a folder of mail.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Mailbox {
|
||||
pub folder: Folder,
|
||||
pub collection: Vec<Envelope>,
|
||||
|
@ -49,10 +47,21 @@ pub struct Mailbox {
|
|||
pub threads: Vec<Container>,
|
||||
}
|
||||
|
||||
impl Clone for Mailbox {
|
||||
fn clone(&self) -> Self {
|
||||
Mailbox {
|
||||
folder: self.folder.clone(),
|
||||
collection: self.collection.clone(),
|
||||
threaded_collection: self.threaded_collection.clone(),
|
||||
threads: self.threads.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mailbox {
|
||||
pub fn new_dummy() -> Self {
|
||||
Mailbox {
|
||||
folder: Folder::default(),
|
||||
folder: folder_default(),
|
||||
collection: Vec::with_capacity(0),
|
||||
threaded_collection: Vec::with_capacity(0),
|
||||
threads: Vec::with_capacity(0),
|
||||
|
@ -67,7 +76,7 @@ impl Mailbox {
|
|||
collection.sort_by(|a, b| a.date().cmp(&b.date()));
|
||||
let (threads, threaded_collection) = build_threads(&mut collection, sent_folder);
|
||||
Ok(Mailbox {
|
||||
folder: folder.clone(),
|
||||
folder: (*folder).clone(),
|
||||
collection: collection,
|
||||
threads: threads,
|
||||
threaded_collection: threaded_collection,
|
||||
|
|
11
src/bin.rs
11
src/bin.rs
|
@ -59,9 +59,7 @@ fn make_input_thread(
|
|||
sx.send(ThreadEvent::Input(k));
|
||||
},
|
||||
|| {
|
||||
sx.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(
|
||||
UIMode::Fork,
|
||||
)));
|
||||
sx.send(ThreadEvent::UIEvent(UIEventType::ChangeMode(UIMode::Fork)));
|
||||
},
|
||||
&rx,
|
||||
)
|
||||
|
@ -104,9 +102,10 @@ fn main() {
|
|||
let b = Entity {
|
||||
component: Box::new(listing),
|
||||
};
|
||||
let window = Entity {
|
||||
component: Box::new(VSplit::new(menu, b, 90, true)),
|
||||
};
|
||||
let mut tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true))]));
|
||||
tabs.add_component(Box::new(Composer {}));
|
||||
let window = Entity { component: tabs };
|
||||
|
||||
let status_bar = Entity {
|
||||
component: Box::new(StatusBar::new(window)),
|
||||
};
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
#![cfg(feature = "python")]
|
||||
use pyo3::prelude::*;
|
||||
|
||||
|
||||
#[pymodinit(pythonmeli)]
|
||||
fn pythonmeli(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
// pyo3 aware function. All of our python interface could be declared in a separate module.
|
||||
// Note that the `#[pyfn()]` annotation automatically converts the arguments from
|
||||
// Python objects to Rust values; and the Rust return value back into a Python object.
|
||||
#[pyfn(m, "sum_as_string")]
|
||||
fn sum_as_string_py(_py: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
let out = sum_as_string(a, b);
|
||||
Ok(out)
|
||||
fn sum_as_string_py(_py: Python, a: i64, b: i64) -> PyResult<String> {
|
||||
let out = sum_as_string(a, b);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// logic implemented as a normal rust function
|
||||
fn sum_as_string(a:i64, b:i64) -> String {
|
||||
fn sum_as_string(a: i64, b: i64) -> String {
|
||||
format!("{}", a + b).to_string()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* meli - ui crate
|
||||
*
|
||||
* Copyright 2017-2018 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/>.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Composer {}
|
||||
|
||||
impl fmt::Display for Composer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO display subject/info
|
||||
write!(f, "compose")
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Composer {
|
||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
clear_area(grid, area);
|
||||
context.dirty_areas.push_back(area);
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn set_dirty(&mut self) {}
|
||||
}
|
|
@ -0,0 +1,679 @@
|
|||
/*
|
||||
* meli - ui crate.
|
||||
*
|
||||
* Copyright 2017-2018 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/>.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
const MAX_COLS: usize = 500;
|
||||
|
||||
/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the thread's content in a
|
||||
/// `ThreadView`.
|
||||
pub struct CompactMailListing {
|
||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||
cursor_pos: (usize, usize, usize),
|
||||
new_cursor_pos: (usize, usize, usize),
|
||||
length: usize,
|
||||
sort: (SortField, SortOrder),
|
||||
//subsort: (SortField, SortOrder),
|
||||
/// Cache current view.
|
||||