Add sidebar menu

embed
Manos Pitsidianakis 2018-07-11 23:12:25 +03:00
parent c141496038
commit b11eb1e494
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
6 changed files with 134 additions and 35 deletions

View File

@ -28,6 +28,33 @@ use std::io;
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Debug, Default, Clone)]
pub struct Folder {
name: String,
path: String,
children: Vec<usize>,
}
impl Folder {
fn new(path: String, file_name: String, children: Vec<usize>) -> Self {
Folder {
name: file_name,
path: path,
children: children,
}
}
pub fn get_path(&self) -> &str {
&self.path
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn get_children(&self) -> &Vec<usize> {
&self.children
}
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct FileAccount { struct FileAccount {
folders: String, folders: String,
@ -43,7 +70,8 @@ struct FileSettings {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AccountSettings { pub struct AccountSettings {
pub folders: Vec<String>, name: String,
pub folders: Vec<Folder>,
format: String, format: String,
pub sent_folder: String, pub sent_folder: String,
threaded: bool, threaded: bool,
@ -53,6 +81,9 @@ impl AccountSettings {
pub fn get_format(&self) -> &str { pub fn get_format(&self) -> &str {
&self.format &self.format
} }
pub fn get_name(&self) -> &str {
&self.name
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -94,7 +125,8 @@ impl Settings {
for (id, x) in fs.accounts { for (id, x) in fs.accounts {
let mut folders = Vec::new(); let mut folders = Vec::new();
fn recurse_folders<P: AsRef<Path>>(folders: &mut Vec<String>, p: P) { 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 mut f in fs::read_dir(p).unwrap() {
for f in f.iter_mut() { for f in f.iter_mut() {
{ {
@ -105,21 +137,27 @@ impl Settings {
continue; continue;
} }
if path.is_dir() { if path.is_dir() {
folders.push(path.to_str().unwrap().to_string()); let path_children = recurse_folders(folders, &path);
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 path = PathBuf::from(&x.folders); let path = PathBuf::from(&x.folders);
let path_children = recurse_folders(&mut folders, &path);
if path.is_dir() { if path.is_dir() {
folders.push(path.to_str().unwrap().to_string()); folders.push(Folder::new(path.to_str().unwrap().to_string(), path.file_name().unwrap().to_str().unwrap().to_string(), path_children));
} }
recurse_folders(&mut folders, &x.folders); //recurse_folders(&mut folders, &x.folders);
eprintln!("folders is {:?}", folders);
s.insert( s.insert(
id.clone(), id.clone(),
AccountSettings { AccountSettings {
name: id.clone(),
folders: folders, folders: folders,
format: x.format.to_lowercase(), format: x.format.to_lowercase(),
sent_folder: x.sent_folder.clone(), sent_folder: x.sent_folder.clone(),

View File

@ -21,7 +21,7 @@
use mailbox::*; use mailbox::*;
use mailbox::backends::{RefreshEventConsumer, Backends}; use mailbox::backends::{RefreshEventConsumer, Backends};
use conf::AccountSettings; use conf::{AccountSettings, Folder};
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
#[derive(Debug)] #[derive(Debug)]
@ -42,7 +42,7 @@ impl Account {
let sent_folder = settings let sent_folder = settings
.folders .folders
.iter() .iter()
.position(|x| *x == settings.sent_folder); .position(|x| *x.get_path() == settings.sent_folder);
let mut folders = Vec::with_capacity(settings.folders.len()); let mut folders = Vec::with_capacity(settings.folders.len());
for _ in 0..settings.folders.len() { for _ in 0..settings.folders.len() {
folders.push(None); folders.push(None);
@ -59,15 +59,18 @@ impl Account {
} }
} }
pub fn watch(&self, r: RefreshEventConsumer) -> () { pub fn watch(&self, r: RefreshEventConsumer) -> () {
self.backend.watch(r, &self.settings.folders); self.backend.watch(r, &self.settings.folders[..]);
} }
/* This doesn't represent the number of correctly parsed mailboxes though */ /* This doesn't represent the number of correctly parsed mailboxes though */
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.folders.len() self.folders.len()
} }
pub fn list_folders(&self) -> Vec<String> { pub fn list_folders(&self) -> Vec<Folder> {
self.settings.folders.clone() self.settings.folders.clone()
} }
pub fn get_name(&self) -> &str {
&self.name
}
} }
impl Index<usize> for Account { impl Index<usize> for Account {
@ -81,12 +84,13 @@ impl IndexMut<usize> for Account {
fn index_mut(&mut self, index: usize) -> &mut Option<Result<Mailbox>> { fn index_mut(&mut self, index: usize) -> &mut Option<Result<Mailbox>> {
if self.folders[index].is_none() { if self.folders[index].is_none() {
eprintln!("building folder {:?}", self.settings.folders[index]); eprintln!("building folder {:?}", self.settings.folders[index]);
let path = self.settings.folders[index].clone(); let folder = &self.settings.folders[index];
let path = folder.get_path().clone();
if self.sent_folder.is_some() { if self.sent_folder.is_some() {
let id = self.sent_folder.unwrap(); let id = self.sent_folder.unwrap();
if id == index { if id == index {
eprintln!("building sent_folder.."); eprintln!("building sent_folder..");
self.folders[index] = Some(Mailbox::new(&path, &None, self.backend.get(&path))); self.folders[index] = Some(Mailbox::new(folder, &None, self.backend.get(&folder)));
eprintln!("Done!"); eprintln!("Done!");
} else { } else {
eprintln!( eprintln!(
@ -103,13 +107,13 @@ impl IndexMut<usize> for Account {
) )
} }
}; };
let sent_path = self.settings.folders[id].clone(); let sent_path = &self.settings.folders[id];
if sent[0].is_none() { if sent[0].is_none() {
eprintln!("\tbuilding sent_folder.."); eprintln!("\tbuilding sent_folder..");
sent[0] = Some(Mailbox::new(&sent_path, &None, self.backend.get(&path))); sent[0] = Some(Mailbox::new(sent_path, &None, self.backend.get(&folder)));
eprintln!("\tDone!"); eprintln!("\tDone!");
} }
cur[0] = Some(Mailbox::new(&path, &sent[0],self.backend.get(&path))); cur[0] = Some(Mailbox::new(folder, &sent[0],self.backend.get(folder)));
eprintln!("Done!"); eprintln!("Done!");
} }
} else { } else {
@ -117,7 +121,7 @@ impl IndexMut<usize> for Account {
"Now building folder {:?} without sent_folder", "Now building folder {:?} without sent_folder",
self.settings.folders[index] self.settings.folders[index]
); );
self.folders[index] = Some(Mailbox::new(&path, &None,self.backend.get(&path))); self.folders[index] = Some(Mailbox::new(folder, &None,self.backend.get(&folder)));
eprintln!("Done!"); eprintln!("Done!");
}; };
} }

View File

@ -23,6 +23,7 @@ use mailbox::email::{Envelope, Flag};
use error::{MeliError, Result}; use error::{MeliError, Result};
use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer}; use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend, RefreshEvent, RefreshEventConsumer};
use mailbox::email::parser; use mailbox::email::parser;
use conf::Folder;
extern crate notify; extern crate notify;
@ -122,8 +123,8 @@ pub struct MaildirType {
impl MailBackend for MaildirType { impl MailBackend for MaildirType {
fn get(&self, path: &str) -> Result<Vec<Envelope>> { fn get(&self, folder: &Folder) -> Result<Vec<Envelope>> {
self.get_multicore(4, path) self.get_multicore(4, folder)
/* /*
MaildirType::is_valid(&self.path)?; MaildirType::is_valid(&self.path)?;
@ -146,7 +147,7 @@ impl MailBackend for MaildirType {
Ok(r) Ok(r)
*/ */
} }
fn watch(&self, sender: RefreshEventConsumer, folders: &[String]) -> () { fn watch(&self, sender: RefreshEventConsumer, folders: &[Folder]) -> () {
let folders = folders.to_vec(); let folders = folders.to_vec();
thread::Builder::new().name("folder watch".to_string()).spawn(move || { thread::Builder::new().name("folder watch".to_string()).spawn(move || {
@ -156,7 +157,7 @@ impl MailBackend for MaildirType {
if MaildirType::is_valid(&f).is_err() { if MaildirType::is_valid(&f).is_err() {
continue; continue;
} }
let mut p = PathBuf::from(&f); let mut p = PathBuf::from(&f.get_path());
p.push("cur"); p.push("cur");
watcher.watch(&p, RecursiveMode::NonRecursive).unwrap(); watcher.watch(&p, RecursiveMode::NonRecursive).unwrap();
p.pop(); p.pop();
@ -182,7 +183,8 @@ impl MaildirType {
path: path.to_string(), path: path.to_string(),
} }
} }
fn is_valid(path: &str) -> Result<()> { fn is_valid(f: &Folder) -> Result<()> {
let path = f.get_path();
let mut p = PathBuf::from(path); let mut p = PathBuf::from(path);
for d in &["cur", "new", "tmp"] { for d in &["cur", "new", "tmp"] {
p.push(d); p.push(d);
@ -195,8 +197,9 @@ impl MaildirType {
} }
Ok(()) Ok(())
} }
pub fn get_multicore(&self, cores: usize, path: &str) -> Result<Vec<Envelope>> { pub fn get_multicore(&self, cores: usize, folder: &Folder) -> Result<Vec<Envelope>> {
MaildirType::is_valid(path)?; MaildirType::is_valid(folder)?;
let path = folder.get_path();
let mut path = PathBuf::from(path); let mut path = PathBuf::from(path);
path.push("cur"); path.push("cur");
let iter = path.read_dir()?; let iter = path.read_dir()?;

View File

@ -20,6 +20,7 @@
*/ */
pub mod maildir; pub mod maildir;
use conf::Folder;
use mailbox::email::{Envelope, Flag}; use mailbox::email::{Envelope, Flag};
use mailbox::backends::maildir::MaildirType; use mailbox::backends::maildir::MaildirType;
use error::Result; use error::Result;
@ -79,8 +80,8 @@ impl RefreshEventConsumer {
} }
} }
pub trait MailBackend: ::std::fmt::Debug { pub trait MailBackend: ::std::fmt::Debug {
fn get(&self, path: &str) -> Result<Vec<Envelope>>; fn get(&self, folder: &Folder) -> Result<Vec<Envelope>>;
fn watch(&self, sender:RefreshEventConsumer, folders: &[String]) -> (); fn watch(&self, sender:RefreshEventConsumer, folders: &[Folder]) -> ();
//fn new(folders: &Vec<String>) -> Box<Self>; //fn new(folders: &Vec<String>) -> Box<Self>;
//login function //login function
} }

View File

@ -30,13 +30,15 @@ pub use mailbox::accounts::Account;
pub mod thread; pub mod thread;
pub use mailbox::thread::{build_threads, Container}; pub use mailbox::thread::{build_threads, Container};
use conf::Folder;
use std::option::Option; use std::option::Option;
/// `Mailbox` represents a folder of mail. /// `Mailbox` represents a folder of mail.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Mailbox { pub struct Mailbox {
pub path: String, pub folder: Folder,
pub collection: Vec<Envelope>, pub collection: Vec<Envelope>,
pub threaded_collection: Vec<usize>, pub threaded_collection: Vec<usize>,
threads: Vec<Container>, threads: Vec<Container>,
@ -46,18 +48,18 @@ pub struct Mailbox {
impl Mailbox { impl Mailbox {
pub fn new_dummy() -> Self { pub fn new_dummy() -> Self {
Mailbox { Mailbox {
path: String::default(), folder: Folder::default(),
collection: Vec::with_capacity(0), collection: Vec::with_capacity(0),
threaded_collection: Vec::with_capacity(0), threaded_collection: Vec::with_capacity(0),
threads: Vec::with_capacity(0), threads: Vec::with_capacity(0),
} }
} }
pub fn new(path: &str, sent_folder: &Option<Result<Mailbox>>, collection: Result<Vec<Envelope>>) -> Result<Mailbox> { pub fn new(folder: &Folder, sent_folder: &Option<Result<Mailbox>>, collection: Result<Vec<Envelope>>) -> Result<Mailbox> {
let mut collection: Vec<Envelope> = collection?; let mut collection: Vec<Envelope> = collection?;
collection.sort_by(|a, b| a.get_date().cmp(&b.get_date())); collection.sort_by(|a, b| a.get_date().cmp(&b.get_date()));
let (threads, threaded_collection) = build_threads(&mut collection, sent_folder); let (threads, threaded_collection) = build_threads(&mut collection, sent_folder);
Ok(Mailbox { Ok(Mailbox {
path: path.to_string(), folder: folder.clone(),
collection: collection, collection: collection,
threads: threads, threads: threads,
threaded_collection: threaded_collection, threaded_collection: threaded_collection,

View File

@ -48,7 +48,8 @@ impl MailListing {
/// Draw only the list of `Envelope`s. /// Draw only the list of `Envelope`s.
fn draw_list(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) { fn draw_list(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
if self.length == 0 { if self.length == 0 {
write_string_to_grid(&format!("Folder `{}` is empty.", self.mailbox.path), grid, Color::Default, Color::Default, upper_left, upper_left); clear_area(grid, upper_left, bottom_right);
write_string_to_grid(&format!("Folder `{}` is empty.", self.mailbox.folder.get_name()), grid, Color::Default, Color::Default, upper_left, upper_left);
return; return;
} }
/* If cursor position has changed, remove the highlight from the previous position and /* If cursor position has changed, remove the highlight from the previous position and
@ -228,8 +229,9 @@ impl Component for MailListing {
#[derive(Debug)] #[derive(Debug)]
pub struct AccountMenu { pub struct AccountMenu {
entries: Vec<(usize, String)>, entries: Vec<(usize, Folder)>,
dirty: bool, dirty: bool,
name: String,
} }
@ -237,30 +239,79 @@ impl AccountMenu {
pub fn new(account: &Account) -> Self{ pub fn new(account: &Account) -> Self{
let mut entries = Vec::with_capacity(account.len()); let mut entries = Vec::with_capacity(account.len());
for (idx, acc) in account.list_folders().iter().enumerate() { for (idx, acc) in account.list_folders().iter().enumerate() {
eprintln!("acc[{}] is {:?}", idx, acc);
entries.push((idx, acc.clone())); entries.push((idx, acc.clone()));
} }
AccountMenu { AccountMenu {
entries: entries, entries: entries,
dirty: true, dirty: true,
name: account.get_name().to_string(),
} }
} }
} }
impl Component for AccountMenu { impl Component for AccountMenu {
fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) { fn draw(&mut self, grid: &mut CellBuffer, upper_left: Pos, bottom_right: Pos) {
eprintln!("reached accountmenu draw {:?}", self);
if !(self.dirty) { if !(self.dirty) {
return; return;
} }
self.dirty = false; self.dirty = false;
let mut parents: Vec<Option<usize>> = vec!(None; self.entries.len());
for (idx, e) in self.entries.iter().enumerate() {
for c in e.1.get_children() {
parents[*c] = Some(idx);
}
}
let mut roots = Vec::new();
for (idx, c) in parents.iter().enumerate() {
if c.is_none() {
roots.push(idx);
}
}
let mut ind = 0;
let mut depth = String::from("");
let mut s = String::from(format!("{}\n", self.name));
fn pop(depth: &mut String) {
depth.pop();
depth.pop();
depth.pop();
depth.pop();
}
fn push(depth: &mut String, c: char) {
depth.push(' ');
depth.push(c);
depth.push(' ');
depth.push(' ');
}
fn print(root: usize, parents: &Vec<Option<usize>>, depth: &mut String, entries: &Vec<(usize, Folder)>, mut s: String) -> String {
let len = s.len();
s.insert_str(len, &format!("{}: {}\n", &entries[root].1.get_name(), entries[root].0));
let children_no = entries[root].1.get_children().len();
for (idx, child) in entries[root].1.get_children().iter().enumerate() {
let len = s.len();
s.insert_str(len, &format!("{}├─", depth));
push(depth, if idx == children_no - 1 {'│'} else { ' ' });
s = print(*child, parents, depth, entries, s);
pop(depth);
}
s
}
for r in roots {
s =print(r, &parents, &mut depth, &self.entries, s);
}
let lines: Vec<&str> = s.lines().collect();
let mut idx = 0; let mut idx = 0;
for y in get_y(upper_left)..get_y(bottom_right) { for y in get_y(upper_left)..get_y(bottom_right) {
if idx == self.entries.len() { if idx == self.entries.len() {
break; break;
} }
let s = format!("{:?}",self.entries[idx]); let s = format!("{}", lines[idx]);
eprintln!("wrote {} to menu", s);
write_string_to_grid(&s, grid, Color::Red, Color::Default, set_y(upper_left, y), bottom_right); write_string_to_grid(&s, grid, Color::Red, Color::Default, set_y(upper_left, y), bottom_right);
idx += 1; idx += 1;
} }