summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-01-15 12:31:49 +0200
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-01-20 15:58:59 +0200
commit7f8c6383610c5c8595b99a35b359fe366592e195 (patch)
tree58bdf7eadef9d5e94de0133e58efc6a4e5dc9f5a /ui
parent853fe14128751a20ace66db999d6b9b12ddca646 (diff)
melib/imap: add mailbox creation ability
Diffstat (limited to 'ui')
-rw-r--r--ui/src/conf/accounts.rs199
-rw-r--r--ui/src/execute.rs44
-rw-r--r--ui/src/lib.rs1
-rw-r--r--ui/src/plugins/backend.rs4
-rw-r--r--ui/src/state.rs8
-rw-r--r--ui/src/types.rs2
6 files changed, 178 insertions, 80 deletions
diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs
index 116d1200..67df5e2a 100644
--- a/ui/src/conf/accounts.rs
+++ b/ui/src/conf/accounts.rs
@@ -384,7 +384,6 @@ impl Account {
folder_names.insert(f.hash(), f.path().to_string());
}
- let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new();
let mut tree: Vec<FolderNode> = Vec::new();
let mut collection: Collection = Collection::new(Default::default());
for (h, f) in ref_folders.iter() {
@@ -392,28 +391,6 @@ impl Account {
/* Skip unsubscribed folder */
continue;
}
- if f.parent().is_none() {
- fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode {
- let mut node = FolderNode {
- hash: h,
- kids: Vec::new(),
- };
- for &c in ref_folders[&h].children() {
- node.kids.push(rec(c, ref_folders));
- }
- node
- };
-
- tree.push(rec(*h, &ref_folders));
- for &c in f.children() {
- stack.push(c);
- }
- while let Some(next) = stack.pop() {
- for c in ref_folders[&next].children() {
- stack.push(*c);
- }
- }
- }
folders.insert(
*h,
MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0),
@@ -430,40 +407,7 @@ impl Account {
collection.threads.insert(*h, Threads::default());
}
- tree.sort_unstable_by(|a, b| {
- if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
- std::cmp::Ordering::Greater
- } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
- std::cmp::Ordering::Less
- } else {
- ref_folders[&a.hash]
- .path()
- .cmp(&ref_folders[&b.hash].path())
- }
- });
-
- let mut stack: SmallVec<[Option<&FolderNode>; 8]> = SmallVec::new();
- for n in tree.iter_mut() {
- folders_order.push(n.hash);
- n.kids.sort_unstable_by(|a, b| {
- if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
- std::cmp::Ordering::Greater
- } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
- std::cmp::Ordering::Less
- } else {
- ref_folders[&a.hash]
- .path()
- .cmp(&ref_folders[&b.hash].path())
- }
- });
- stack.extend(n.kids.iter().rev().map(Some));
- while let Some(Some(next)) = stack.pop() {
- folders_order.push(next.hash);
- stack.extend(next.kids.iter().rev().map(Some));
- }
- }
- drop(stack);
-
+ build_folders_order(&folder_confs, &mut tree, &ref_folders, &mut folders_order);
self.folders = folders;
self.ref_folders = ref_folders;
self.folder_confs = folder_confs;
@@ -784,11 +728,7 @@ impl Account {
self.folders.is_empty()
}
pub fn list_folders(&self) -> Vec<Folder> {
- let mut folders = if let Ok(folders) = self.backend.read().unwrap().folders() {
- folders
- } else {
- return Vec::new();
- };
+ let mut folders = self.ref_folders.clone();
let folder_confs = &self.folder_confs;
//debug!("folder renames: {:?}", folder_renames);
for f in folders.values_mut() {
@@ -1015,7 +955,68 @@ impl Account {
}
pub fn folder_operation(&mut self, path: &str, op: FolderOperation) -> Result<()> {
- Err(MeliError::new("Not implemented."))
+ match op {
+ FolderOperation::Create => {
+ if self.settings.account.read_only() {
+ Err(MeliError::new("Account is read-only."))
+ } else {
+ let mut folder = self
+ .backend
+ .write()
+ .unwrap()
+ .create_folder(path.to_string())?;
+ let mut new = FileFolderConf::default();
+ new.folder_conf.subscribe = super::ToggleFlag::InternalVal(true);
+ new.folder_conf.usage = if folder.special_usage() != SpecialUsageMailbox::Normal
+ {
+ Some(folder.special_usage())
+ } else {
+ let tmp = SpecialUsageMailbox::detect_usage(folder.name());
+ if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None {
+ let _ = folder.set_special_usage(tmp.unwrap());
+ }
+ tmp
+ };
+
+ self.folder_confs.insert(folder.hash(), new);
+ self.folder_names
+ .insert(folder.hash(), folder.path().to_string());
+ self.folders.insert(
+ folder.hash(),
+ MailboxEntry::Parsing(
+ Mailbox::new(folder.clone(), &FnvHashMap::default()),
+ 0,
+ 0,
+ ),
+ );
+ self.workers.insert(
+ folder.hash(),
+ Account::new_worker(
+ folder.clone(),
+ &mut self.backend,
+ &self.work_context,
+ self.notify_fn.clone(),
+ ),
+ );
+ self.collection
+ .threads
+ .insert(folder.hash(), Threads::default());
+ self.ref_folders.insert(folder.hash(), folder);
+ build_folders_order(
+ &self.folder_confs,
+ &mut self.tree,
+ &self.ref_folders,
+ &mut self.folders_order,
+ );
+ Ok(())
+ }
+ }
+ FolderOperation::Delete => Err(MeliError::new("Not implemented.")),
+ FolderOperation::Subscribe => Err(MeliError::new("Not implemented.")),
+ FolderOperation::Unsubscribe => Err(MeliError::new("Not implemented.")),
+ FolderOperation::Rename(_) => Err(MeliError::new("Not implemented.")),
+ FolderOperation::SetPermissions(_) => Err(MeliError::new("Not implemented.")),
+ }
}
pub fn folder_confs(&self, folder_hash: FolderHash) -> &FileFolderConf {
@@ -1147,3 +1148,75 @@ impl IndexMut<usize> for Account {
self.folders.get_mut(&self.folders_order[index]).unwrap()
}
}
+
+fn build_folders_order(
+ folder_confs: &FnvHashMap<FolderHash, FileFolderConf>,
+ tree: &mut Vec<FolderNode>,
+ ref_folders: &FnvHashMap<FolderHash, Folder>,
+ folders_order: &mut Vec<FolderHash>,
+) {
+ let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new();
+ tree.clear();
+ folders_order.clear();
+ for (h, f) in ref_folders.iter() {
+ if !folder_confs.contains_key(&h) {
+ continue;
+ }
+
+ if f.parent().is_none() {
+ fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode {
+ let mut node = FolderNode {
+ hash: h,
+ kids: Vec::new(),
+ };
+ for &c in ref_folders[&h].children() {
+ node.kids.push(rec(c, ref_folders));
+ }
+ node
+ };
+
+ tree.push(rec(*h, &ref_folders));
+ for &c in f.children() {
+ stack.push(c);
+ }
+ while let Some(next) = stack.pop() {
+ for c in ref_folders[&next].children() {
+ stack.push(*c);
+ }
+ }
+ }
+ }
+
+ tree.sort_unstable_by(|a, b| {
+ if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
+ std::cmp::Ordering::Greater
+ } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
+ std::cmp::Ordering::Less
+ } else {
+ ref_folders[&a.hash]
+ .path()
+ .cmp(&ref_folders[&b.hash].path())
+ }
+ });
+
+ let mut stack: SmallVec<[Option<&FolderNode>; 8]> = SmallVec::new();
+ for n in tree.iter_mut() {
+ folders_order.push(n.hash);
+ n.kids.sort_unstable_by(|a, b| {
+ if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
+ std::cmp::Ordering::Greater
+ } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
+ std::cmp::Ordering::Less
+ } else {
+ ref_folders[&a.hash]
+ .path()
+ .cmp(&ref_folders[&b.hash].path())
+ }
+ });
+ stack.extend(n.kids.iter().rev().map(Some));
+ while let Some(Some(next)) = stack.pop() {
+ folders_order.push(next.hash);
+ stack.extend(next.kids.iter().rev().map(Some));
+ }
+ }
+}
diff --git a/ui/src/execute.rs b/ui/src/execute.rs
index 2d50686a..e6932e45 100644
--- a/ui/src/execute.rs
+++ b/ui/src/execute.rs
@@ -23,7 +23,7 @@
*/
use melib::backends::FolderOperation;
pub use melib::thread::{SortField, SortOrder};
-use nom::{digit, not_line_ending};
+use nom::{digit, not_line_ending, IResult};
use std;
pub mod actions;
pub mod history;
@@ -45,6 +45,26 @@ macro_rules! define_commands {
};
}
+pub fn quoted_argument(input: &[u8]) -> IResult<&[u8], &str> {
+ if input.is_empty() {
+ return IResult::Error(nom::ErrorKind::Custom(0));
+ }
+
+ if input[0] == b'"' {
+ let mut i = 1;
+ while i < input.len() {
+ if input[i] == b'\"' && input[i - 1] != b'\\' {
+ return IResult::Done(&input[i + 1..], unsafe {
+ std::str::from_utf8_unchecked(&input[1..i])
+ });
+ }
+ i += 1;
+ }
+ return IResult::Error(nom::ErrorKind::Custom(0));
+ } else {
+ return map_res!(input, is_not!(" "), std::str::from_utf8);
+ }
+}
define_commands!([
{ tags: ["set"],
desc: "set [seen/unseen], toggles message's Seen flag.",
@@ -179,9 +199,9 @@ define_commands!([
ws!(tag!("pipe"))
>> bin: map_res!(is_not!(" "), std::str::from_utf8)
>> is_a!(" ")
- >> args: separated_list!(is_a!(" "), is_not!(" "))
+ >> args: separated_list!(is_a!(" "), quoted_argument)
>> ({
- View(Pipe(bin.to_string(), args.into_iter().map(|v| String::from_utf8(v.to_vec()).unwrap()).collect::<Vec<String>>()))
+ View(Pipe(bin.to_string(), args.into_iter().map(String::from).collect::<Vec<String>>()))
})) | do_parse!(
ws!(tag!("pipe"))
>> bin: ws!(map_res!(is_not!(" "), std::str::from_utf8))
@@ -233,7 +253,7 @@ define_commands!([
named!( create_folder<Action>,
do_parse!(
ws!(tag!("create-folder"))
- >> account: map_res!(is_not!(" "), std::str::from_utf8)
+ >> account: quoted_argument
>> is_a!(" ")
>> path: map_res!(call!(not_line_ending), std::str::from_utf8)
>> (Folder(account.to_string(), path.to_string(), FolderOperation::Create))
@@ -247,7 +267,7 @@ define_commands!([
named!( sub_folder<Action>,
do_parse!(
ws!(tag!("subscribe-folder"))
- >> account: map_res!(is_not!(" "), std::str::from_utf8)
+ >> account: quoted_argument
>> is_a!(" ")
>> path: map_res!(call!(not_line_ending), std::str::from_utf8)
>> (Folder(account.to_string(), path.to_string(), FolderOperation::Subscribe))
@@ -261,7 +281,7 @@ define_commands!([
named!( unsub_folder<Action>,
do_parse!(
ws!(tag!("unsubscribe-folder"))
- >> account: map_res!(is_not!(" "), std::str::from_utf8)
+ >> account: quoted_argument
>> is_a!(" ")
>> path: map_res!(call!(not_line_ending), std::str::from_utf8)
>> (Folder(account.to_string(), path.to_string(), FolderOperation::Unsubscribe))
@@ -275,9 +295,9 @@ define_commands!([
named!( rename_folder<Action>,
do_parse!(
ws!(tag!("rename-folder"))
- >> account: map_res!(is_not!(" "), std::str::from_utf8)
+ >> account: quoted_argument
>> is_a!(" ")
- >> src: map_res!(is_not!(" "), std::str::from_utf8)
+ >> src: quoted_argument
>> is_a!(" ")
>> dest: map_res!(call!(not_line_ending), std::str::from_utf8)
>> (Folder(account.to_string(), src.to_string(), FolderOperation::Rename(dest.to_string())))
@@ -291,9 +311,9 @@ define_commands!([
named!( delete_folder<Action>,
do_parse!(
ws!(tag!("delete-folder"))
- >> account: map_res!(is_not!(" "), std::str::from_utf8)
+ >> account: quoted_argument
>> is_a!(" ")
- >> path: map_res!(call!(not_line_ending), std::str::from_utf8)
+ >> path: quoted_argument
>> (Folder(account.to_string(), path.to_string(), FolderOperation::Delete))
)
);
@@ -305,7 +325,7 @@ define_commands!([
named!( reindex<Action>,
do_parse!(
ws!(tag!("reindex"))
- >> account: map_res!(is_not!(" "), std::str::from_utf8)
+ >> account: quoted_argument
>> (AccountAction(account.to_string(), ReIndex))
)
);
@@ -329,7 +349,7 @@ define_commands!([
do_parse!(
ws!(tag!("save-attachment"))
>> idx: map_res!(map_res!(is_not!(" "), std::str::from_utf8), usize::from_str)
- >> path: ws!(map_res!(call!(not_line_ending), std::str::from_utf8))
+ >> path: ws!(quoted_argument)
>> (View(SaveAttachment(idx, path.to_string())))
)
);
diff --git a/ui/src/lib.rs b/ui/src/lib.rs
index 98d23ed6..7ec3ad48 100644
--- a/ui/src/lib.rs
+++ b/ui/src/lib.rs
@@ -231,7 +231,6 @@ pub mod timer {
impl PosixTimer {
pub fn rearm(&mut self) {
- debug!("posixtimer rearm");
let spec = itimerspec {
it_interval: timespec {
tv_sec: self.interval.as_secs().try_into().unwrap_or(0),
diff --git a/ui/src/plugins/backend.rs b/ui/src/plugins/backend.rs
index e1062ca0..4731a1ee 100644
--- a/ui/src/plugins/backend.rs
+++ b/ui/src/plugins/backend.rs
@@ -102,7 +102,7 @@ impl MailBackend for PluginBackend {
channel.expect_ack().unwrap();
loop {
let read_val: Result<PluginResult<Option<Vec<SimpleEnvelope>>>> =
- debug!(channel.from_read());
+ channel.from_read();
match read_val.map(Into::into).and_then(std::convert::identity) {
Ok(Some(a)) => {
tx.send(AsyncStatus::Payload(Ok(a
@@ -300,7 +300,7 @@ impl BackendOp for PluginOp {
debug!(channel.expect_ack())?;
channel.write_ref(&rmpv::ValueRef::Integer(self.hash.into()))?;
debug!(channel.expect_ack())?;
- let bytes: Result<PluginResult<String>> = debug!(channel.from_read());
+ let bytes: Result<PluginResult<String>> = channel.from_read();
self.bytes = Some(bytes.map(Into::into).and_then(std::convert::identity)?);
if let Some(ref bytes) = self.bytes {
debug!(Envelope::from_bytes(bytes.as_bytes(), None));
diff --git a/ui/src/state.rs b/ui/src/state.rs
index 1067f2e0..518c242f 100644
--- a/ui/src/state.rs
+++ b/ui/src/state.rs
@@ -254,7 +254,6 @@ impl State {
let sender = sender;
loop {
thread::park();
- debug!("unparked");
sender.send(ThreadEvent::Pulse).unwrap();
thread::sleep(std::time::Duration::from_millis(100));
@@ -586,6 +585,13 @@ impl State {
self.context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(e.to_string()),
));
+ } else {
+ self.context.replies.push_back(UIEvent::StatusEvent(
+ StatusEvent::DisplayMessage(format!(
+ "{} succesfully created in `{}`",
+ path, account_name
+ )),
+ ));
}
} else {
self.context.replies.push_back(UIEvent::StatusEvent(
diff --git a/ui/src/types.rs b/ui/src/types.rs
index e8c76ec6..c2e2a985 100644
--- a/ui/src/types.rs
+++ b/ui/src/types.rs
@@ -181,7 +181,7 @@ pub mod segment_tree {
let height = (f64::from(u32::try_from(val.len()).unwrap_or(0)))
.log2()
.ceil() as u32;
- let max_size = 2 * (2_usize.pow(height)) - 1;
+ let max_size = 2 * (2_usize.pow(height));
let mut segment_tree: SmallVec<[u8; 1024]> =
SmallVec::from_iter(core::iter::repeat(0).take(max_size));